We help IT Professionals succeed at work.

We've partnered with Certified Experts, Carl Webster and Richard Faulkner, to bring you a podcast all about Citrix Workspace, moving to the cloud, and analytics & intelligence. Episode 2 coming soon!Listen Now

x

Cross-thread problem with Delegate

biplavo
biplavo asked
on
Medium Priority
551 Views
Last Modified: 2011-10-03
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(Nothing, 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.Clear()
            While drSz.Read
                Dim chkSz As New CheckBox
                chkSz.Name = drSz.GetString(0)
                chkSz.Text = drSz.GetString(1)
                cmbSz.Items.Add(drSz.GetString(1))
                SzAdderPnl.Controls.Add(chkSz)
            End While
         Catch ex As Exception
            MsgBox(ex.ToString)
        Finally
            drSz.Close()
            cnOrd.Close()
        End Try
    End Sub


 
Comment
Watch Question

Mike TomlinsonHigh School Computer Science, Computer Applications, Digital Design, and Mathematics Teacher
CERTIFIED EXPERT
Top Expert 2009

Commented:
Try something like...

    Private T As System.Threading.Thread = Nothing
    Private Delegate Sub AddControlDelegate(ByVal ctl As Control)
    Private Delegate Sub DoneDelegate()

    Private Sub tbItm_SelectedIndexChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles tbItm.SelectedIndexChanged
        If Me.tbItm.SelectedIndex = 1 Then
            If T Is Nothing Then
                Me.tbItm.Enabled = False
                Me.SzAdderPnl.Controls.Clear()
                T = New System.Threading.Thread(AddressOf sbGuiLoader)
                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.GetString(1))
                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(AddressOf 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

Author

Commented:
Sir,
What does the following lines of code mean or do:


  If Me.InvokeRequired Then
            Me.Invoke(New AddControlDelegate(AddressOf AddControl), New Object() {ctl})

End If
Mike TomlinsonHigh School Computer Science, Computer Applications, Digital Design, and Mathematics Teacher
CERTIFIED EXPERT
Top Expert 2009

Commented:
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(AddressOf 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(AddressOf 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(AddressOf 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...  =)

Author

Commented:
Dear Sir,

Now the problems occurs here:
cmbSz.Items.Add(drSz.GetString(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.

Mike TomlinsonHigh School Computer Science, Computer Applications, Digital Design, and Mathematics Teacher
CERTIFIED EXPERT
Top Expert 2009

Commented:
Can you show us the code you are using now?...

Author

Commented:
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.Clear()
            While drSz.Read
                Dim chkSz As New CheckBox
                chkSz.Name = drSz.GetString(0)
                chkSz.Text = drSz.GetString(1)
                cmbSz.Items.Add(drSz.GetString(1))
                AddControl(chkSz)

                'SzAdderPnl.Controls.Add(chkSz)
            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(AddressOf 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.Clear()
                T = New System.Threading.Thread(AddressOf sbGuiLoader)
                T.Start()
            End If
        ElseIf tbItm.SelectedIndex = 1 Then
.....................

       ElseIf tbItm.SelectedIndex = 2 Then
.........................      

        ElseIf tbItm.SelectedIndex = 3 Then
      ......................

        End If
    End Sub
High School Computer Science, Computer Applications, Digital Design, and Mathematics Teacher
CERTIFIED EXPERT
Top Expert 2009
Commented:
Ah...I'm sorry...I see it now.

"chkSz" is not the same as "cmbSz".   =)

We can pass that info along with the control in the Delegate:

    Private Delegate Sub AddControlDelegate(ByVal ctl As Control, ByVal value As String)

    Private Sub AddControl(ByVal ctl As Control, ByVal value As String)
        If Me.InvokeRequired Then
            Me.Invoke(New AddControlDelegate(AddressOf AddControl), New Object() {ctl, value})
        Else
            cmbSz.Items.Add(value)
            Me.SzAdderPnl.Controls.Add(ctl)
        End If
    End Sub

Then in your loop, remove that line and change the call:

            While drSz.Read
                Dim chkSz As New CheckBox
                chkSz.Name = drSz.GetString(0)
                chkSz.Text = drSz.GetString(1)
                AddControl(chkSz, drSz.GetString(1))
            End While

Also, this line:

    Me.SzAdderPnl.Controls.Clear()

should not be in your sbGuiLoader() sub.
 

Not the solution you were looking for? Getting a personalized solution is easy.

Ask the Experts

Author

Commented:
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.
Mike TomlinsonHigh School Computer Science, Computer Applications, Digital Design, and Mathematics Teacher
CERTIFIED EXPERT
Top Expert 2009

Commented:
Just google for:

    vb.net thread marshal delegate invoke

or:
   
    vb.net multithreading gui
Access more of Experts Exchange with a free account
Thanks for using Experts Exchange.

Create a free account to continue.

Limited access with a free account allows you to:

  • View three pieces of content (articles, solutions, posts, and videos)
  • Ask the experts questions (counted toward content limit)
  • Customize your dashboard and profile

*This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.

OR

Please enter a first name

Please enter a last name

8+ characters (letters, numbers, and a symbol)

By clicking, you agree to the Terms of Use and Privacy Policy.