DataGridView InvalidOperationException

I have a datagridview whose datasource is a bindingsource linked to a datatable in a dataset.

From a background worker, I want to add rows to the table then refresh the datagridview to reflect the updates.

Here's what I have so far:
 
Private Delegate Sub AddEntryCallback(ByVal Entry As SearchResultEntry)
  Private Delegate Sub RefreshDisplayCallback()

  Private Sub bwPageSearch_DoWork(ByVal sender As Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles bwPageSearch.DoWork
     
'.....'

      Dim AddEntry As AddEntryCallback = AddressOf AddEntryToTable
      Dim UpdateDisplay As RefreshDisplayCallback = AddressOf RefreshDisplay

'.....'

          For Each entry As SearchResultEntry In Response.Entries
            If dgvADSI.InvokeRequired Then
              AddEntry.Invoke(entry)
            Else
              AddEntryToTable(entry)
            End If
            If dgvADSI.InvokeRequired Then
              UpdateDisplay.Invoke()
            Else
              RefreshDisplay()
            End If
          Next

'.....'

  End Sub

  Private Sub AddEntryToTable(ByVal Entry As SearchResultEntry)

    Try
      Dim tmpDataRow As DataRow = dtADSI.NewRow

      If Entry.Attributes.Contains("displayname") Then tmpDataRow.Item("Name") = Entry.Attributes.Item("displayname").Item(0).ToString
      If Entry.Attributes.Contains("mail") Then tmpDataRow.Item("E-Mail Address") = Entry.Attributes.Item("mail").Item(0).ToString
      If Entry.Attributes.Contains("telephonenumber") Then tmpDataRow.Item("Business Phone") = Entry.Attributes.Item("telephonenumber").Item(0).ToString
      If Entry.Attributes.Contains("homephone") Then tmpDataRow.Item("Home Phone") = Entry.Attributes.Item("homephone").Item(0).ToString
      If Not String.IsNullOrEmpty(Entry.DistinguishedName.Trim) Then tmpDataRow.Item("adsPath") = Entry.DistinguishedName.ToString
      dtADSI.Rows.Add(tmpDataRow)
      dtADSI.AcceptChanges()
      tmpDataRow = Nothing
    Catch ex As Exception
      MsgBox("AddEntryToTable: " & NewLine() & ex.Message)
    End Try

  End Sub 

  Private Sub RefreshDisplay()

    Try
      If dtADSI.Rows.Count > 0 Then
        Try
          If Not dgvADSI.Visible Then
            'Cross-thread errors happen on any of these lines of code'
            Me.MaximumSize = New Size(intResultsMaxFrmWt, intResultsMaxFrmHt)
            Me.MinimumSize = New Size(intResultsFrmWt, intResultsFrmHt)
            dgvADSI.AutoResizeColumns()
            dgvAHCS.Visible = False
            dgvADSI.Visible = True
          End If
        Catch ex As Exception
          MsgBox("Not dgvADSI.Visible: " & NewLine() & ex.Message)
        End Try
        dtADSI.AcceptChanges()
        Try
          dgvADSI.Invalidate()
        Catch ex As Exception
          MsgBox("dgvADSI.Invalidate: " & NewLine() & ex.Message)
        End Try
      End If
    Catch ex As Exception
      MsgBox("dtADSI.Rows.Count: " & NewLine() & ex.Message)
    End Try

  End Sub

Open in new window


Problem is, I'm getting Cross-thread errors when the code tries to resize the form or modify the datagridview.  I know that the section of code is being invoked, due to debugging I've put in.  What am I missing and need to do to get this to work?
LVL 7
rogerardAsked:
Who is Participating?
 
rogerardConnect With a Mentor Author Commented:
Correction - Replace:
updateUI(Me, "MaximumSize", New Size(intResultsMaxFrmWt, intResultsMaxFrmHt))
          updateUI(Me, "MinimumSize", New Size(intResultsFrmWt, intResultsFrmHt))
          If dgvADSI.InvokeRequired Then
            dgvADSI.Invoke(New AutoResizeColumnsInvoker(AddressOf dgvADSI.AutoResizeColumns))
          Else
            dgvADSI.AutoResizeColumns()
          End If
          updateUI(dgvAHCS, "Visible", False)
          updateUI(dgvADSI, "Visible", True)
          dgvADSI.Sort(dgvADSI.Columns(0), System.ComponentModel.ListSortDirection.Ascending)

Open in new window

With:
 
updateUI(Me, "MaximumSize", New Size(intResultsMaxFrmWt, intResultsMaxFrmHt))
            updateUI(Me, "MinimumSize", New Size(intResultsFrmWt, intResultsFrmHt))
            updateUI(dgvAHCS, "Visible", False)
            updateUI(dgvADSI, "Visible", True)
              If dgvADSI.InvokeRequired Then
                dgvADSI.Invoke(New AutoResizeColumnsInvoker(AddressOf dgvADSI.AutoResizeColumns))
                dgvADSI.Invoke(New SortInvoker(AddressOf dgvADSI.Sort), dgvADSI.Columns(0), System.ComponentModel.ListSortDirection.Ascending)
              Else
                dgvADSI.AutoResizeColumns()
                dgvADSI.Sort(dgvADSI.Columns(0), System.ComponentModel.ListSortDirection.Ascending)
              End If

Open in new window

0
 
rogerardAuthor Commented:
I've made some progress:

I've replaced  
Me.MaximumSize = New Size(intResultsMaxFrmWt, intResultsMaxFrmHt)
Me.MinimumSize = New Size(intResultsFrmWt, intResultsFrmHt)
dgvADSI.AutoResizeColumns()
dgvAHCS.Visible = False
dgvADSI.Visible = True

Open in new window

with  
updateUI(Me, "MaximumSize", New Size(intResultsMaxFrmWt, intResultsMaxFrmHt))
updateUI(Me, "MinimumSize", New Size(intResultsFrmWt, intResultsFrmHt))
dgvADSI.AutoResizeColumns(DataGridViewAutoSizeColumnsMode.AllCells)
updateUI(dgvAHCS, "Visible", False)
updateUI(dgvADSI, "Visible", True)

Open in new window

Private Delegate Sub UpdateUICallback(ByVal ctrl As Control, ByVal prop As String, ByVal value As Object)
  
Private Sub updateUI(ByVal ctrl As Control, ByVal prop As String, ByVal value As Object)
    Try
      If ctrl.InvokeRequired = True Then
        Dim d As New UpdateUICallback(AddressOf updateUI)
        ctrl.Invoke(d, ctrl, prop, value)
      Else
        ctrl.GetType.GetProperty(prop).SetValue(ctrl, value, Nothing)
      End If
    Catch ex As Exception
      MsgBox(ex.Message)
    End Try
  End Sub

Open in new window

The invalidate didn't work, so I also replaced
dgvADSI.Invalidate
with
dgvADSI.Refresh()
Now, the crossthread error occurs on the refresh.  
0
 
rogerardAuthor Commented:
More progress.

I replaced
dgvADSI.Refresh()
With
ctlMethod(dgvADSI, "refresh")

Now, there's no exceptions, but the datagridview still is not refreshing.
Private Delegate Sub ctlMethodCallback(ByVal ctrl As Control, ByVal Method As String)
  Private Sub ctlMethod(ByVal ctrl As Control, ByVal Method As String)

    Try
      If ctrl.InvokeRequired Then
        Try
          Dim d As New ctlMethodCallback(AddressOf ctlMethod)
          ctrl.Invoke(d, ctrl, Method)
        Catch ex As Exception
          MsgBox("Unable to invoke: " & NewLine & ex.Message)
        End Try
      Else
        ctrl.GetType.GetMethod(Method)
      End If
    Catch ex As Exception
      MsgBox("Unable to appy method: " & NewLine & ex.Message)
    End Try

  End Sub

Open in new window

0
The 14th Annual Expert Award Winners

The results are in! Meet the top members of our 2017 Expert Awards. Congratulations to all who qualified!

 
Bob LearnedCommented:
When do you need to update the DataGridView?  If you need to update it after the BackgroundWorker is done, then I would make the call from the RunWorkerCompleted event.
0
 
rogerardAuthor Commented:
I'm trying to update the datagridview as the data is being added to the datatable.  I suspect it has something to with making the datatable sync information across threads, but I'm not sure how to do that.
0
 
Bob LearnedCommented:
How many rows are you getting for the DataGridView?  Is this a long running process, and you want to refresh the DataGridView after each "block" of data?
0
 
rogerardAuthor Commented:
On narrow queries, the results return and populate quickly.  On the broader queries, it can take up to a minute.   I'm trying to replicate, somewhat, the people search form when you click start>Search>People to add a few more capabilities and search locations specific to my company.  I have a active directory search set up using s.ds.p and returning paged results within a background worker, with a page size of 10, for results up to several thousand, possibly, depending on the search criteria.   I'm just not able to get the thread to update the dataset and refresh the datagridview the same way that the MS people search does on large results.

0
 
rogerardConnect With a Mentor Author Commented:
I think I figured it out finally. Here's my final code.


Private Delegate Sub RefreshInvoker()
  Private Delegate Sub AutoResizeColumnsInvoker()

  Private Sub bwPageSearch_DoWork(ByVal sender As Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles bwPageSearch.DoWork
          
          '........'

          For Each entry As SearchResultEntry In Response.Entries
            AddEntryToTable(entry)
          Next

          '........'

End sub

  Private Sub AddEntryToTable(ByVal Entry As SearchResultEntry)

    Try
      Dim tmpDataRow As DataRow = dtADSI.NewRow
      If Entry.Attributes.Contains("displayname") Then tmpDataRow.Item("Name") = Entry.Attributes.Item("displayname").Item(0).ToString
      If Entry.Attributes.Contains("mail") Then tmpDataRow.Item("E-Mail Address") = Entry.Attributes.Item("mail").Item(0).ToString
      If Entry.Attributes.Contains("telephonenumber") Then tmpDataRow.Item("Business Phone") = Entry.Attributes.Item("telephonenumber").Item(0).ToString
      If Entry.Attributes.Contains("homephone") Then tmpDataRow.Item("Home Phone") = Entry.Attributes.Item("homephone").Item(0).ToString
      If Not String.IsNullOrEmpty(Entry.DistinguishedName.Trim) Then tmpDataRow.Item("adsPath") = Entry.DistinguishedName.ToString
      dtADSI.Rows.Add(tmpDataRow)
      dtADSI.AcceptChanges()
      tmpDataRow = Nothing
      RefreshDisplay()
    Catch ex As Exception
      MsgBox("AddEntryToTable: " & NewLine() & ex.Message)
    End Try

  End Sub

  Private Sub RefreshDisplay()

    Try
      If dtADSI.Rows.Count > 0 Then
        If Not dgvADSI.Visible Then
          updateUI(Me, "MaximumSize", New Size(intResultsMaxFrmWt, intResultsMaxFrmHt))
          updateUI(Me, "MinimumSize", New Size(intResultsFrmWt, intResultsFrmHt))
          If dgvADSI.InvokeRequired Then
            dgvADSI.Invoke(New AutoResizeColumnsInvoker(AddressOf dgvADSI.AutoResizeColumns))
          Else
            dgvADSI.AutoResizeColumns()
          End If
          updateUI(dgvAHCS, "Visible", False)
          updateUI(dgvADSI, "Visible", True)
          dgvADSI.Sort(dgvADSI.Columns(0), System.ComponentModel.ListSortDirection.Ascending)
        End If
        If dgvADSI.InvokeRequired Then
          dgvADSI.Invoke(New RefreshInvoker(AddressOf dgvADSI.Refresh))
        Else
          dgvADSI.Refresh()
        End If
      End If
    Catch ex As Exception
      MsgBox("dtADSI.Rows.Count: " & NewLine() & ex.Message)
    End Try

  End Sub

  Private Sub RefreshDisplay()

    Try
      If dtADSI.Rows.Count > 0 Then
        If Not dgvADSI.Visible Then
          updateUI(Me, "MaximumSize", New Size(intResultsMaxFrmWt, intResultsMaxFrmHt))
          updateUI(Me, "MinimumSize", New Size(intResultsFrmWt, intResultsFrmHt))
          If dgvADSI.InvokeRequired Then
            dgvADSI.Invoke(New AutoResizeColumnsInvoker(AddressOf dgvADSI.AutoResizeColumns))
          Else
            dgvADSI.AutoResizeColumns()
          End If
          updateUI(dgvAHCS, "Visible", False)
          updateUI(dgvADSI, "Visible", True)
          dgvADSI.Sort(dgvADSI.Columns(0), System.ComponentModel.ListSortDirection.Ascending)
        End If
        If dgvADSI.InvokeRequired Then
          dgvADSI.Invoke(New RefreshInvoker(AddressOf dgvADSI.Refresh))
        Else
          dgvADSI.Refresh()
        End If
      End If
    Catch ex As Exception
      MsgBox("dtADSI.Rows.Count: " & NewLine() & ex.Message)
    End Try

  End Sub

Open in new window

0
 
rogerardAuthor Commented:
Answered my own question.
0
Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.

All Courses

From novice to tech pro — start learning today.