biplavo
asked on
Cross-thread problem with Delegate
I am adding controls in a flowlayout panel at runtime ( calling sbGuiLoader as shown in the code below) but when i call the procedure sbGuiLoader() directly; it takes too much time to add the controls dynamically.
So i have tried to call it on a separate thread by utlizing Delegate so that it wont block the UI but i got an error which says:
"Cross-thread operation not valid: Control 'SzAdderPnl' accessed from a thread other than the thread it was created on".Since I amnot that experienced with Delegates; could you guys help me out please?
---------------Code goes like this-------------------
Public Delegate Sub delLdItm()
Private Sub tbItm_SelectedIndexChanged (ByVal sender As Object, ByVal e As System.EventArgs) Handles tbItm.SelectedIndexChanged
If tbItm.SelectedIndex =1 Then
Dim objDelLdItm As New delLdItm(AddressOf sbGuiLoader)
objDelLdItm.BeginInvoke(No thing, Nothing)
End If
End Sub
Private Sub sbGuiLoader()
Dim cnOrd As OleDbConnection = clsDb.retCn
Dim drSz As OleDbDataReader = Nothing
Try
cnOrd.Close()
cnOrd.Open()
Dim cmd As New OleDbCommand("Select SizeId,ItemSize from tbl_SizeTDef", cnOrd)
drSz = cmd.ExecuteReader
Me.SzAdderPnl.Controls.Cle ar()
While drSz.Read
Dim chkSz As New CheckBox
chkSz.Name = drSz.GetString(0)
chkSz.Text = drSz.GetString(1)
cmbSz.Items.Add(drSz.GetSt ring(1))
SzAdderPnl.Controls.Add(ch kSz)
End While
Catch ex As Exception
MsgBox(ex.ToString)
Finally
drSz.Close()
cnOrd.Close()
End Try
End Sub
So i have tried to call it on a separate thread by utlizing Delegate so that it wont block the UI but i got an error which says:
"Cross-thread operation not valid: Control 'SzAdderPnl' accessed from a thread other than the thread it was created on".Since I amnot that experienced with Delegates; could you guys help me out please?
---------------Code goes like this-------------------
Public Delegate Sub delLdItm()
Private Sub tbItm_SelectedIndexChanged
If tbItm.SelectedIndex =1 Then
Dim objDelLdItm As New delLdItm(AddressOf sbGuiLoader)
objDelLdItm.BeginInvoke(No
End If
End Sub
Private Sub sbGuiLoader()
Dim cnOrd As OleDbConnection = clsDb.retCn
Dim drSz As OleDbDataReader = Nothing
Try
cnOrd.Close()
cnOrd.Open()
Dim cmd As New OleDbCommand("Select SizeId,ItemSize from tbl_SizeTDef", cnOrd)
drSz = cmd.ExecuteReader
Me.SzAdderPnl.Controls.Cle
While drSz.Read
Dim chkSz As New CheckBox
chkSz.Name = drSz.GetString(0)
chkSz.Text = drSz.GetString(1)
cmbSz.Items.Add(drSz.GetSt
SzAdderPnl.Controls.Add(ch
End While
Catch ex As Exception
MsgBox(ex.ToString)
Finally
drSz.Close()
cnOrd.Close()
End Try
End Sub
ASKER
Sir,
What does the following lines of code mean or do:
If Me.InvokeRequired Then
Me.Invoke(New AddControlDelegate(Address Of AddControl), New Object() {ctl})
End If
What does the following lines of code mean or do:
If Me.InvokeRequired Then
Me.Invoke(New AddControlDelegate(Address
End If
The AddControl() sub is called from the thread we created right? So the calling sub is "T":
Private Sub AddControl(ByVal ctl As Control)
If Me.InvokeRequired Then
Me.Invoke(New AddControlDelegate(Address Of AddControl), New Object() {ctl})
Else
Me.SzAdderPnl.Controls.Add (ctl)
End If
End Sub
The first line:
If Me.InvokeRequired Then
Determines if "Me" was created on the same thread as the caller. "Me" refers to the Form which was created by the main UI thread. Since "T" is not the main UI thread, InvokeRequired() returns True.
The next line is then executed:
Me.Invoke(New AddControlDelegate(Address Of AddControl), New Object() {ctl})
The Invoke() method takes a Delegate and runs that Delegate on the thread that created the caller. The caller in this case is "Me" (the main UI thread) because we used:
Me.Invoke(...)
A Delegate is simply a "pointer to a function". Here we create a pointer to the AddControl() sub:
New AddControlDelegate(Address Of AddControl)
This is the sub that we are already in thus making this a RECURSIVE call. So the Invoke() method is going to make a recursive call to AddControl() and we pass the parameters from the first call to the second call by creating an array of object:
New Object() {ctl}
and passing that to Invoke() as well.
This is KEY...the first time AddControl() is called it is from thread "T", but the second time it is called (via Me.Invoke) it is called from the main UI thread.
So we enter AddControl() AGAIN (from the recursive call) and hit the first line once more:
If Me.InvokeRequired Then
But this time InvokeRequired() returns False because "Me" was infact created on the same thread as the caller. The caller this time is the main UI thread.
Since we are on the same thread as the main UI it is now SAFE to modify or add controls to the GUI.
Now the second half of the If...Then statement is executed:
Me.SzAdderPnl.Controls.Add (ctl)
And hopefully at this point we have gotten rid of your "Cross-thread operation not valid" error.
Note that recursion is NOT a requirement here. That is simply the way I chose to do it. We could have created a "helper" sub to receive the control to add and made the Delegate point to that instead.
Does that help at all? It seems long winded and confusing when I write it all down but when I look at the code now it just looks "right" to me.
Yell at me if you need more or a different explanation... =)
Private Sub AddControl(ByVal ctl As Control)
If Me.InvokeRequired Then
Me.Invoke(New AddControlDelegate(Address
Else
Me.SzAdderPnl.Controls.Add
End If
End Sub
The first line:
If Me.InvokeRequired Then
Determines if "Me" was created on the same thread as the caller. "Me" refers to the Form which was created by the main UI thread. Since "T" is not the main UI thread, InvokeRequired() returns True.
The next line is then executed:
Me.Invoke(New AddControlDelegate(Address
The Invoke() method takes a Delegate and runs that Delegate on the thread that created the caller. The caller in this case is "Me" (the main UI thread) because we used:
Me.Invoke(...)
A Delegate is simply a "pointer to a function". Here we create a pointer to the AddControl() sub:
New AddControlDelegate(Address
This is the sub that we are already in thus making this a RECURSIVE call. So the Invoke() method is going to make a recursive call to AddControl() and we pass the parameters from the first call to the second call by creating an array of object:
New Object() {ctl}
and passing that to Invoke() as well.
This is KEY...the first time AddControl() is called it is from thread "T", but the second time it is called (via Me.Invoke) it is called from the main UI thread.
So we enter AddControl() AGAIN (from the recursive call) and hit the first line once more:
If Me.InvokeRequired Then
But this time InvokeRequired() returns False because "Me" was infact created on the same thread as the caller. The caller this time is the main UI thread.
Since we are on the same thread as the main UI it is now SAFE to modify or add controls to the GUI.
Now the second half of the If...Then statement is executed:
Me.SzAdderPnl.Controls.Add
And hopefully at this point we have gotten rid of your "Cross-thread operation not valid" error.
Note that recursion is NOT a requirement here. That is simply the way I chose to do it. We could have created a "helper" sub to receive the control to add and made the Delegate point to that instead.
Does that help at all? It seems long winded and confusing when I write it all down but when I look at the code now it just looks "right" to me.
Yell at me if you need more or a different explanation... =)
ASKER
Dear Sir,
Now the problems occurs here:
cmbSz.Items.Add(drSz.GetSt ring(1))
When I change the tab index and came back again....
it produces an error message which says....
Cross-thread operation not valid: Control 'CmbSz' accessed from a thread other than the thread it was created on.
Now the problems occurs here:
cmbSz.Items.Add(drSz.GetSt
When I change the tab index and came back again....
it produces an error message which says....
Cross-thread operation not valid: Control 'CmbSz' accessed from a thread other than the thread it was created on.
Can you show us the code you are using now?...
ASKER
Here goes the total code supported by your code.......
Private T As System.Threading.Thread = Nothing
Private Delegate Sub AddControlDelegate(ByVal ctl As Control)
Private Delegate Sub DoneDelegate()
Private Sub sbGuiLoader()
Dim cnOrd As OleDbConnection = clsDb.retCn
Dim drSz As OleDbDataReader = Nothing
Try
cnOrd.Close()
cnOrd.Open()
Dim cmd As New OleDbCommand("Select SizeId,ItemSize from tbl_SizeTDef ", cnOrd)
'-----------Load Defined Size in "Add New Food Type"---------'
drSz = cmd.ExecuteReader
Me.SzAdderPnl.Controls.Cle ar()
While drSz.Read
Dim chkSz As New CheckBox
chkSz.Name = drSz.GetString(0)
chkSz.Text = drSz.GetString(1)
cmbSz.Items.Add(drSz.GetSt ring(1))
AddControl(chkSz)
'SzAdderPnl.Controls.Add(c hkSz)
End While
'-------------End Load---------------------- ---------- -------'
Catch ex As Exception
MsgBox(ex.ToString)
Finally
drSz.Close()
cnOrd.Close()
Done()
End Try
End Sub
Private Sub AddControl(ByVal ctl As Control)
If Me.InvokeRequired Then
Me.Invoke(New AddControlDelegate(Address Of AddControl), New Object() {ctl})
Else
Me.SzAdderPnl.Controls.Add (ctl)
End If
End Sub
Private Sub Done()
If Me.InvokeRequired Then
Me.Invoke(New DoneDelegate(AddressOf Done))
Else
tbItm.Enabled = True
T = Nothing
End If
End Sub
Private Sub tbItm_SelectedIndexChanged (ByVal sender As Object, ByVal e As System.EventArgs) Handles tbItm.SelectedIndexChanged
If tbItm.SelectedIndex = 0 Then
tbItm.Size = New Size(454, 399)
If T Is Nothing Then
Me.tbItm.Enabled = False
Me.SzAdderPnl.Controls.Cle ar()
T = New System.Threading.Thread(Ad dressOf sbGuiLoader)
T.Start()
End If
ElseIf tbItm.SelectedIndex = 1 Then
.....................
ElseIf tbItm.SelectedIndex = 2 Then
.........................
ElseIf tbItm.SelectedIndex = 3 Then
......................
End If
End Sub
Private T As System.Threading.Thread = Nothing
Private Delegate Sub AddControlDelegate(ByVal ctl As Control)
Private Delegate Sub DoneDelegate()
Private Sub sbGuiLoader()
Dim cnOrd As OleDbConnection = clsDb.retCn
Dim drSz As OleDbDataReader = Nothing
Try
cnOrd.Close()
cnOrd.Open()
Dim cmd As New OleDbCommand("Select SizeId,ItemSize from tbl_SizeTDef ", cnOrd)
'-----------Load Defined Size in "Add New Food Type"---------'
drSz = cmd.ExecuteReader
Me.SzAdderPnl.Controls.Cle
While drSz.Read
Dim chkSz As New CheckBox
chkSz.Name = drSz.GetString(0)
chkSz.Text = drSz.GetString(1)
cmbSz.Items.Add(drSz.GetSt
AddControl(chkSz)
'SzAdderPnl.Controls.Add(c
End While
'-------------End Load----------------------
Catch ex As Exception
MsgBox(ex.ToString)
Finally
drSz.Close()
cnOrd.Close()
Done()
End Try
End Sub
Private Sub AddControl(ByVal ctl As Control)
If Me.InvokeRequired Then
Me.Invoke(New AddControlDelegate(Address
Else
Me.SzAdderPnl.Controls.Add
End If
End Sub
Private Sub Done()
If Me.InvokeRequired Then
Me.Invoke(New DoneDelegate(AddressOf Done))
Else
tbItm.Enabled = True
T = Nothing
End If
End Sub
Private Sub tbItm_SelectedIndexChanged
If tbItm.SelectedIndex = 0 Then
tbItm.Size = New Size(454, 399)
If T Is Nothing Then
Me.tbItm.Enabled = False
Me.SzAdderPnl.Controls.Cle
T = New System.Threading.Thread(Ad
T.Start()
End If
ElseIf tbItm.SelectedIndex = 1 Then
.....................
ElseIf tbItm.SelectedIndex = 2 Then
.........................
ElseIf tbItm.SelectedIndex = 3 Then
......................
End If
End Sub
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
ASKER
Thank You for spending time for my question.
I find this 'Area' pretty interesting; i want to explore more on this topic; so beside this method what should I "Google" for to do it on a different way.
I find this 'Area' pretty interesting; i want to explore more on this topic; so beside this method what should I "Google" for to do it on a different way.
Just google for:
vb.net thread marshal delegate invoke
or:
vb.net multithreading gui
vb.net thread marshal delegate invoke
or:
vb.net multithreading gui
Private T As System.Threading.Thread = Nothing
Private Delegate Sub AddControlDelegate(ByVal ctl As Control)
Private Delegate Sub DoneDelegate()
Private Sub tbItm_SelectedIndexChanged
If Me.tbItm.SelectedIndex = 1 Then
If T Is Nothing Then
Me.tbItm.Enabled = False
Me.SzAdderPnl.Controls.Cle
T = New System.Threading.Thread(Ad
T.Start()
End If
End If
End Sub
Private Sub sbGuiLoader()
Dim cnOrd As OleDbConnection = clsDb.retCn
Dim drSz As OleDbDataReader = Nothing
Try
cnOrd.Close()
cnOrd.Open()
Dim cmd As New OleDbCommand("Select SizeId,ItemSize from tbl_SizeTDef", cnOrd)
drSz = cmd.ExecuteReader
While drSz.Read
Dim chkSz As New CheckBox
chkSz.Name = drSz.GetString(0)
chkSz.Text = drSz.GetString(1)
cmbSz.Items.Add(drSz.GetSt
AddControl(chkSz)
End While
Catch ex As Exception
MsgBox(ex.ToString)
Finally
drSz.Close()
cnOrd.Close()
Done()
End Try
End Sub
Private Sub AddControl(ByVal ctl As Control)
If Me.InvokeRequired Then
Me.Invoke(New AddControlDelegate(Address
Else
Me.SzAdderPnl.Controls.Add
End If
End Sub
Private Sub Done()
If Me.InvokeRequired Then
Me.Invoke(New DoneDelegate(AddressOf Done))
Else
tbItm.Enabled = True
T = Nothing
End If
End Sub