2

I have been looking for BindingList Invoke from other thread few days ago but not able to get a proper solution in vb.net , most of them are in C# but i am finding difficult to understand that. Hence i have created a small application which has 2 Forms (Form1 and Form2) and one Class, Form1 will be main UI thread and Form2 will be running on a different thread.

Form1 has a DataGrindView bound to shared BindingList(of T) and a Button, once the Button clicked Form2 will be loaded on a different thread.

Here is Form1 Codes :

Imports System.ComponentModel
Imports System.Threading
Public Class Form1
    Public Shared ListOfNames As BindingList(Of Names) = New BindingList(Of Names)

    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        DataGridView1.DataSource = ListOfNames
        DataGridView1.Columns("FullName").DataPropertyName = "FullName"
    End Sub

    Private Sub Button1_Click_1(sender As Object, e As EventArgs) Handles Button1.Click
        Dim thread As Threading.Thread = New Threading.Thread(AddressOf loadQuoteForm)
        thread.SetApartmentState((ApartmentState.STA))
        thread.Start()
    End Sub

    Private Sub loadQuoteForm()
        Dim SecondForm As Form2 = New Form2
        Application.Run(SecondForm)
    End Sub
End Class

Form2 just has a button, once clicked will create a Names class instance , change its one property and try to add to Form1.BindingList(of T).

Here is Form2 Codes:

Public Class Form2
    Private Sub btnTestFromDiffTread_Click(sender As Object, e As EventArgs) Handles btnTestFromDiffTread.Click
        Try
            Dim myName As Names = New Names
            myName.FullName = "John Peter"
            Form1.ListOfNames.Add(myName)
        Catch ex As Exception
            MsgBox(ex.ToString)
        End Try
    End Sub
End Class


Imports System.ComponentModel
Public Class Names
    Implements System.ComponentModel.INotifyPropertyChanged
    Public Event PropertyChanged(sender As Object, e As System.ComponentModel.PropertyChangedEventArgs) Implements System.ComponentModel.INotifyPropertyChanged.PropertyChanged
    Private _fullName As String
    Public Property FullName() As String
        Get
            Return _fullName
        End Get
        Set(ByVal value As String)
            _fullName = value
            RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs("FullName"))
        End Set
    End Property
End Class

but as you may have guessed as soon as button clicked on Form2 throws exception : enter image description here .

Can anyone please suggest where i should invoke or delegate , sample VB.net codes are very much appreciated

Visual Vincent
  • 18,045
  • 5
  • 28
  • 75
shab
  • 127
  • 9

2 Answers2

1

It is updating of the DataGridView that is causing the issue.

From your comment I can see it is because the DataGridView is bound to DataSource of the DataGridView, so updating this List will cause a cross thread operation if you are doing this on a separate thread.

One solution is to not make ListOfNames public and have a new method to allow adding to this list:

Private Shared ListOfNames As BindingList(Of Names) = New BindingList(Of Names)

Public Sub AddNameToList(newNames As Names)
    DataGridView1.BeginInvoke(Sub() ListOfNames.Add(newNames))
End Sub
Visual Vincent
  • 18,045
  • 5
  • 28
  • 75
Matt Wilko
  • 26,994
  • 10
  • 93
  • 143
  • No, it still shows error after doing suggested fix : - "System.InvalidOperationException: Cross-thread operation not valid: control 'DataGridView1' accessed from a thread other than the thread it was created on." – shab Jul 15 '16 at 14:32
  • No , the error comes at Form2 line:7 which is "Form1.ListOfNames.Add(myName)". ListOfNames is the DataSource for DataGridView1 – shab Jul 15 '16 at 14:41
  • Thank you so much for the swift updates by the way !, seems like its progressing , i have changed as you suggested and now the error message is different but at the same line 7 on Form 2, the error message is : System.InvalidOperationException: Invoke or BeginInvoke cannot be called on a control until the window handle has been created" – shab Jul 15 '16 at 15:04
  • sorry, this was removed already when we created a method to do the invoke, above said error was throwing when that BeginInvoke was there on FormLoad as well as when was not there – shab Jul 15 '16 at 15:16
  • http://stackoverflow.com/questions/808867/invoke-or-begininvoke-cannot-be-called-on-a-control-until-the-window-handle-has – Matt Wilko Jul 15 '16 at 15:17
  • Thanks Matt Wilko your suggestions and that link helped me to sort it out, i am posting final codes here is it helps anyone, – shab Jul 15 '16 at 22:37
0

Finally after every one's suggestion and testing below changes on Form2 button click as well as adding few methods on the Form1 solved the issue:

Added there methods on Form1

Private Shared ListOfNames As BindingList(Of Names) = New BindingList(Of Names)
Private Delegate Sub AddNameToListDelegate(newName As Names)

Public Sub InvokeANDCreateHandle(ByVal newNames As Names)
    Try
        If Me.IsHandleCreated = False Then
            Me.CreateHandle()
        End If
        DataGridView1.Invoke(New AddNameToListDelegate(AddressOf AddNameToList), newNames)
    Catch ex As Exception
        MsgBox(ex.ToString)
    End Try
End Sub

Private Sub AddNameToList(name As Names)
    ListOfNames.Add(name)
End Sub

And Changed Button Click on From2 to :

 Private Sub btnTestFromDiffTread_Click(sender As Object, e As EventArgs) Handles btnTestFromDiffTread.Click
    Try
        Dim myName As Names = New Names
        myName.FullName = "John Peter"
        Form1.InvokeANDCreateHandle(myName)
    Catch ex As Exception
        MsgBox(ex.ToString)
    End Try
 End Sub
shab
  • 127
  • 9