Search code examples
vb.netgridviewbackgroundworker

Update GridView by a BackgroundWorker Completion makes Grid non interactive


I am trying to update GridView on completion of a BackgroundWorker, For the first time it works correctly, but if try execute worker again, data will be assigned to the grid but i could not select a Row on the GridView on UI level and also vertical scroll is now shown. If try to double click cells several times then vertical scroll will appear and i could select any row.

enter image description here

Please refer the VB.Net Code

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

Solution

  • Because you are accessing the DataGridView1 of UI thread from the Worker thread you get the weird behaviour.

    I tested your small app with this code and I got the normal expected behaviour.

    I modified your loadCustomerGrid method and added another Method and Delegate method.

     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)
    

    Explanation:

    "The way to safely access controls from worker threads is via delegation. First you test the InvokeRequired property of the control, which will tell you whether or not you can safely access the control. InvokeRequired is one of the few members of the Control class that is thread-safe, so you can access it anywhere. If the property is True then an invocation is required to access the control because the current method is executing on a thread other than the one that owns the control's Handle.

    The invocation is performed by calling the control's Invoke or BeginInvoke method. You create a delegate, which is an object that contains a reference to a method. It is good practice to make that a reference to the current method. You then pass that delegate to the Invoke or BeginInvoke method. That will essentially call the referenced method again, this time on the thread that owns the control's Handle."

    Source: jmcilhinney post Accessing Controls from Worker Threads http://www.vbforums.com/showthread.php?498387-Accessing-Controls-from-Worker-Threads