VB.net跨线程操作无效

VB.net跨线程操作无效,vb.net,visual-studio-2010,listview,backgroundworker,Vb.net,Visual Studio 2010,Listview,Backgroundworker,我正在尽我最大的努力来解决我遇到的这个错误: 跨线程操作无效:从创建控件“ListView1”的线程以外的线程访问控件“ListView1” 我有一个后台工作人员,他从excel工作表中提取单元格并将其放入listview 在加载表单时,我会这样做: Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load Call createListVi

我正在尽我最大的努力来解决我遇到的这个错误:

跨线程操作无效:从创建控件“ListView1”的线程以外的线程访问控件“ListView1”

我有一个后台工作人员,他从excel工作表中提取单元格并将其放入listview

在加载表单时,我会这样做:

Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
    Call createListView()
End Sub

Private Sub createListView()
    ListView1.View = View.Details
    ListView1.GridLines = True
    ListView1.FullRowSelect = True
    ListView1.HideSelection = False
    ListView1.MultiSelect = False

    ListView1.Columns.Add("Column Name", 150)
    ListView1.Columns.Add("Column Number", 150)
End Sub
然后,在用户选择文件后,我调用backgroundworker:

If openFileDialog1.ShowDialog() = DialogResult.OK Then
        stFilePathAndName = openFileDialog1.FileName
        ProgressBar1.Style = ProgressBarStyle.Marquee
        BGWxml2excel = New System.ComponentModel.BackgroundWorker
        BGWxml2excel.WorkerReportsProgress = True
        BGWxml2excel.WorkerSupportsCancellation = True
        BGWxml2excel.RunWorkerAsync()
End If
然后我处理获取excel列计数和值的过程,以便用它填充listview:

Private Sub BGWxml2excel_DoWork(ByVal sender As Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles BGWxml2excel.DoWork
    Call xml2Excel(stFilePathAndName)
End Sub

Private Sub xml2Excel(ByRef theDirOfFile As String)
    Dim xlsApp As Excel.Application
    Dim xlsWB As Excel.Workbook
    Dim xlsSheet As Excel.Worksheet
    Dim columnCount As Integer = 0

    xlsApp = New Excel.Application
    xlsApp.Visible = False
    xlsApp.DisplayAlerts = False

    xlsWB = xlsApp.Workbooks.OpenXML(Filename:=theDirOfFile, LoadOption:=XlXmlLoadOption.xlXmlLoadImportToList)
    xlsSheet = xlsWB.Worksheets(1)
    xlsSheet.Select()
    columnCount = xlsSheet.UsedRange.Columns.Count

    Dim lvi As New ListViewItem
    Dim x As Integer = 1

    Do Until x = columnCount + 1
        lvi.Text = xlsSheet.Cells(1, x).value
        lvi.SubItems.Add(x)

        ListView1.Items.Add(lvi)
        x = x + 1
    Loop

    'xlsSheet.SaveAs("c:\_tempExcelFile.xlsx", FileFormat:=51, CreateBackup:=False)
    xlsWB.Close()
    xlsApp.Quit()    
End Sub
错误在这一行:

ListView1.Items.Add(lvi)
我能做些什么来纠正这个奇怪的问题

谢谢


David

问题是只有UI线程才能更新UI。由于您正在从工作人员向ListView添加一个项目,因此会出现此异常

要解决此问题,可以将要添加到listview的项存储在共享变量中,UI线程和工作线程都可以访问该变量,在表单上放置计时器,以便UI线程点击勾号事件处理程序,并在勾号处理程序中添加项

淡化C中的示例,因为它对我来说要快得多:-:

private List<ListViewItem> _itemsToBeAdded = new List<ListViewItem>();
private readonly object _lockObject = new object();

// worker method:
private void xml2Excel(string input)
{
    // do some processing...
    ListViewItem lvi = new ListViewItem();
    // set up lvi

    lock(_lockObject)
    {
        _itemsToBeAdded.Add(lvi);
    }
}

private void timer1_Tick(object sender, EventArgs e)
{
    lock(_lockObject)
    {
        foreach(var item in _itemsToBeAdded)
        {
            ListView1.Add(item);
        }
    }
}

问题是只有UI线程可以更新UI。由于您正在从工作人员向ListView添加一个项目,因此会出现此异常

要解决此问题,可以将要添加到listview的项存储在共享变量中,UI线程和工作线程都可以访问该变量,在表单上放置计时器,以便UI线程点击勾号事件处理程序,并在勾号处理程序中添加项

淡化C中的示例,因为它对我来说要快得多:-:

private List<ListViewItem> _itemsToBeAdded = new List<ListViewItem>();
private readonly object _lockObject = new object();

// worker method:
private void xml2Excel(string input)
{
    // do some processing...
    ListViewItem lvi = new ListViewItem();
    // set up lvi

    lock(_lockObject)
    {
        _itemsToBeAdded.Add(lvi);
    }
}

private void timer1_Tick(object sender, EventArgs e)
{
    lock(_lockObject)
    {
        foreach(var item in _itemsToBeAdded)
        {
            ListView1.Add(item);
        }
    }
}

只有UI线程才能访问UI。你的后台工作人员在另一个线程上。您需要使用.invokererequired/.Invoke返回UI线程

后台工作程序的事件处理程序将在主线程上引发;您可以在那里安全地更改UI。但实际上在后台线程中,您必须调用。像这样:

Delegate Sub SetTextCallback([text] As String)

Private Sub SetText(ByVal [text] As String)

        ' InvokeRequired required compares the thread ID of the
        ' calling thread to the thread ID of the creating thread.
        ' If these threads are different, it returns true.
        If Me.textBox1.InvokeRequired Then
            Dim d As New SetTextCallback(AddressOf SetText)
            Me.Invoke(d, New Object() {[text]})
        Else
            Me.textBox1.Text = [text]
        End If
    End Sub

只有UI线程才能访问UI。你的后台工作人员在另一个线程上。您需要使用.invokererequired/.Invoke返回UI线程

后台工作程序的事件处理程序将在主线程上引发;您可以在那里安全地更改UI。但实际上在后台线程中,您必须调用。像这样:

Delegate Sub SetTextCallback([text] As String)

Private Sub SetText(ByVal [text] As String)

        ' InvokeRequired required compares the thread ID of the
        ' calling thread to the thread ID of the creating thread.
        ' If these threads are different, it returns true.
        If Me.textBox1.InvokeRequired Then
            Dim d As New SetTextCallback(AddressOf SetText)
            Me.Invoke(d, New Object() {[text]})
        Else
            Me.textBox1.Text = [text]
        End If
    End Sub