1

我正在尝试在完成 BackgroundWorker 时更新 GridView,这是它第一次正常工作,但如果再次尝试执行 worker,数据将被分配给网格,但我无法在 UI 级别和垂直的 GridView 上选择行现在显示滚动。如果尝试多次双击单元格,则会出现垂直滚动,我可以选择任何行。

在此处输入图像描述

请参考 VB.Net 代码

Public Class Form1

Dim Workers() As BackgroundWorker

Dim dtCustomers As DataTable = New DataTable()
Private dtCustomersLock As New Object
Private dgvCustomersLock As New Object

Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
    dtCustomers.Columns.Add("CustomerID")
    dtCustomers.Columns.Add("CustomerName")
    dtCustomers.Columns.Add("Age")
    LoadWorkers()
End Sub

Private Sub btnLoad_Click(sender As Object, e As EventArgs) Handles btnLoad.Click
    btnClear_Click(sender, e)

    loadCustomerGrid()
    UpdateCustomerGrid()
End Sub

Private Sub LoadWorkers()
    ReDim Workers(1)

    Workers(1) = New BackgroundWorker
    Workers(1).WorkerReportsProgress = True
    Workers(1).WorkerSupportsCancellation = True
    AddHandler Workers(1).DoWork, AddressOf loadCustomerGrid
    AddHandler Workers(1).RunWorkerCompleted, AddressOf UpdateCustomerGrid
End Sub

Private Sub btnLoadThread_Click(sender As Object, e As EventArgs) Handles btnLoadThread.Click

    If Not Workers(1).IsBusy Then
        dtCustomers.Clear()
        Workers(1).RunWorkerAsync()
    End If
End Sub

Private Sub loadCustomerGrid()

    SyncLock dgvCustomersLock
        For i As Integer = 0 To 10
            dtCustomers.Rows.Add(i, "Customer" + i.ToString(), "20" + i.ToString())
        Next
    End SyncLock

    Threading.Thread.Sleep(100)
End Sub

Private Sub UpdateCustomerGrid()
    SyncLock dtCustomersLock
        DataGridView1.DataSource = dtCustomers
        DataGridView1.Focus()
    End SyncLock
End Sub

Private Sub btnClear_Click(sender As Object, e As EventArgs) Handles btnClear.Click
    dtCustomers.Clear()
End Sub
End Class
4

1 回答 1

1

因为您正在从 Worker 线程访问 UI 线程的 DataGridView1,所以您会得到奇怪的行为。

我用这段代码测试了你的小应用程序,我得到了正常的预期行为。

我修改了您的 loadCustomerGrid 方法并添加了另一个 Method 和 Delegate 方法。

 Private Sub loadCustomerGrid()

    SetDataGrid(GridView1)
    Threading.Thread.Sleep(100)
End Sub
 Private Sub setDataGrid(ByVal grd As DataGridView)
    If grd.InvokeRequired Then
        grd.Invoke(New setDataGridInvoker(AddressOf setDataGrid), grd)
    Else
        For i As Integer = 0 To 10
            dtCustomers.Rows.Add(i, "Customer" + i.ToString(), "20" + i.ToString())
        Next
    End If
End Sub
Private Delegate Sub setDataGridInvoker(ByVal grd As DataGridView)

解释:

“从工作线程安全访问控件的方法是通过委托。首先你测试控件的 InvokeRequired 属性,它会告诉你是否可以安全地访问控件。InvokeRequired 是 Control 类的少数成员之一是线程安全的,因此您可以在任何地方访问它。如果属性为 True,则需要调用才能访问控件,因为当前方法是在拥有控件句柄的线程之外的线程上执行的。

通过调用控件的 Invoke 或 BeginInvoke 方法来执行调用。您创建一个委托,它是一个包含对方法的引用的对象。将其作为当前方法的参考是一种很好的做法。然后将该委托传递给 Invoke 或 BeginInvoke 方法。这实际上将再次调用引用的方法,这次是在拥有控件句柄的线程上。”

来源:jmcilhinney 发布从工作线程访问控件http://www.vbforums.com/showthread.php?498387-Accessing-Controls-from-Worker-Threads

于 2013-09-25T09:48:07.247 回答