Threading Problem Updating Datatable and Refreshing DataGridView

rogerard
rogerard used Ask the Experts™
on
I have a DataGridView (dgv) whose DataSource is a BindSource Control whose DataSource is a Dataset and DataMember is a DataTable (dtMain).
 
Private Delegate sub AddEntryToTableCallback(Byval Entry as SearchResultEntry)

Private Sub bwWorker_DoWork(ByVal sender as Object, ByVal e as DoWorkEventArgs) Handles bwWorker.DoWork
  

  Dim AddEntry As New AddEntryToTableCallback(AddressOf AddEntryToTable)

  '...
  '.... Some worker code here...'
  '...

  While True

    For Each entry as SearchResultEntry in Response.Entries
      AddEntry.Invoke(entry)
    Next

  End While

End Sub

Private Sub AddEntryToTable(ByVal Entry as SearchResultEntry)
  Dim tmpDataRow as DataRow = dtMain.NewRow

  '..Assign values to datarow....'

  dtMain.Rows.Add(tmpDataRow)
  dtMain.AcceptChanges()

  if Me.InvokeRequired Then
    Me.BeginInvoke(New MethodInvoker(AddressOf RefreshDisplay))
  Else
    RefreshDisplay()
  End If
End Sub

Private Sub RefreshDisplay()

    If me.InvokeRequired Then
      me.Invoke(New MethodInvoker(AddressOf dgv.Refresh))
    Else
      dgv.Refresh
    End If

End Sub

Open in new window

If I manipulate dgv too much (ie resize, scroll through the added results) while it's still adding, I'll get several of these errors:
 
DataGridView Default Error Dialog

The following exception occured in the DataGridView:

System.InvalidOperationException:  BindingSource cannot be its own data source.  Do not set the DataSource and Datamember Properties to values that refer back to the BindingSource.
  at System.Windows.Forms.BindingSource.get_Count()
  at System.Windows.Forms.CurrencyManager.get_Item(Int32_index)
  at System.Windows.Forms.DataGridView.DataGridViewDataConnection.GetError(Int32 boundColumnIndex, Int32 columnIndex, Int32 rowIndex)

To replace this default dialog please handle the DataError Event.

Open in new window

It isn't exactly clear where this is getting thrown and I've tried adding a catch for the DataError Event, but I can't Isolate it.  The error doesn't seem to hinder the results from continuing to be added.  Nor does the error occur at any time after all rows have been added.  

Does anyone have any thoughts how I can catch and handle/ignore the error?

Thanks
Comment
Watch Question

Do more with

Expert Office
EXPERT OFFICE® is a registered trademark of EXPERTS EXCHANGE®
Most Valuable Expert 2012
Top Expert 2014

Commented:
Try handling the DataError event of both BindingSource and the grid.

Commented:
as above,  handle the datagridview dataerror event. the errors will not simply go away, you can "diagonise" their source/ cause there too

Author

Commented:
The answer was right in front of me, in the original error message.  Thanks for pointing it out!  Based on your suggestions, I added the following code:
 
Private Sub bs_DataError(ByVal sender As Object, ByVal e As BindingManagerDataErrorEventArgs) Handles bs.DataError

  Debug.WriteLine("bs_DataError" & NewLine() & vbTab & e.Exception.Message)

End Sub

Private Sub dgv_DataError(ByVal sender As Object, ByVal e As DataGridViewDataErrorEventArgs) Handles dgv.DataError

  If e.Exception.Message.Contains("BindingSource cannot be its own data source") Then
    e.ThrowException = False 'Threading issue..  data still good'
  Else
    Debug.WriteLine("dgvADSI_DataError" & NewLine() & vbTab & e.Exception.Message)
  End If

End Sub

Open in new window

Those errors are now handled.  But a new/related problem seems to have arisen that I can't seem to diagnose.  If I run the executable outside the IDE, it hangs at the point where the vertical scrollbar gets added to dgv, after adding the 7th row to dtMain.  I attached to the process from the IDE and it's hanging at line 42 of the original code I posted.
dgv.Refresh

Open in new window

Any thoughts?
Learn SQL Server Core 2016

This course will introduce you to SQL Server Core 2016, as well as teach you about SSMS, data tools, installation, server configuration, using Management Studio, and writing and executing queries.

Most Valuable Expert 2012
Top Expert 2014

Commented:
Does it execute that line successfully when run from the VS?

Author

Commented:
Yes, the code appears to run flawlessly from within the IDE, in so far that there are no unhandled exceptions displayed.
Most Valuable Expert 2012
Top Expert 2014

Commented:
That is strange. Try enclosing it in Try Catch block.

Author

Commented:
I didn't put it in the code posted above for brevity, but the original code already contains:
Try 
 '......'
Catch ex as Exception
  MsgBox(ex.Message)
Finally
 'Cleaning up resources here'
End Try

Open in new window

The exception is never getting triggered.

Author

Commented:
I changed
Private Sub RefreshDisplay()

    If me.InvokeRequired Then
      me.Invoke(New MethodInvoker(AddressOf dgv.Refresh))
    Else
      dgv.Refresh
    End If

End Sub

Open in new window

to
Private Sub RefreshDisplay()

    If me.InvokeRequired Then
      me.Invoke(New MethodInvoker(AddressOf dgv.Invalidate))
    Else
      dgv.Invalidate
    End If

End Sub

Open in new window

Now, it doesn't hang after the 7th row, it'll continue adding.  That is, until I try to resize the form, which resizes the dgv, which causes it to hang (outside the IDE, inside in debug mode still runs normal).
When I attach to process and pause execution, it doesn't show me the green/yellow arrow designating the line it's paused on, so I'm at a loss.
Most Valuable Expert 2012
Top Expert 2014

Commented:
Try using SuspendLayout and ResumeLayout functions.

Author

Commented:
I made the following changes.  Now it hangs on line 12 after the 7th row is added.
Private Delegate Sub RefreshCallback(ByVal performLayout As Boolean)

Private Sub RefreshDisplay()

    If me.InvokeRequired Then
        Me.Invoke(New MethodInvoker(AddressOf dgv.SuspendLayout))
        Me.Invoke(New MethodInvoker(AddressOf dgv.Invalidate))
        Me.Invoke(New RefreshCallback(AddressOf dgv.ResumeLayout), True)
    Else
        dgv.SuspendLayout()
        dgv.Invalidate()
        dgv.ResumeLayout(True)
    End If

End Sub

Open in new window

Most Valuable Expert 2012
Top Expert 2014

Commented:
Suspend and Resume layout is used around any operation which is modifying the grid. So

dgv.SuspendLayout)
dgv.Rows.Add(...)
dgv.Rows(0).Cells(0).Value = ...
dgv.ResumeLayout(True)
Commented:
As it turns out, the primary culprit was a threading issue where I was referencing (reading from and assigning to different controls on the form from the worker.  Once I tracked down and properly invoked each reference, I no longer received any exceptions and the program ran without a glitch, even outside the IDE.

Author

Commented:
None of the suggestions provided actually resolved the problem.  I figured out the solution myself.

Do more with

Expert Office
Submit tech questions to Ask the Experts™ at any time to receive solutions, advice, and new ideas from leading industry professionals.

Start 7-Day Free Trial