Solved

Cross-thread problem with Delegate

Posted on 2006-10-27
9
468 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


 
0
Comment
Question by:biplavo
  • 5
  • 4
9 Comments
 
LVL 85

Expert Comment

by:Mike Tomlinson
ID: 17824505
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
0
 

Author Comment

by:biplavo
ID: 17825345
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
0
 
LVL 85

Expert Comment

by:Mike Tomlinson
ID: 17825956
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...  =)
0
 

Author Comment

by:biplavo
ID: 17828906
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.

0
Do You Know the 4 Main Threat Actor Types?

Do you know the main threat actor types? Most attackers fall into one of four categories, each with their own favored tactics, techniques, and procedures.

 
LVL 85

Expert Comment

by:Mike Tomlinson
ID: 17828920
Can you show us the code you are using now?...
0
 

Author Comment

by:biplavo
ID: 17829014
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
0
 
LVL 85

Accepted Solution

by:
Mike Tomlinson earned 125 total points
ID: 17829119
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.
 
0
 

Author Comment

by:biplavo
ID: 17841718
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.
0
 
LVL 85

Expert Comment

by:Mike Tomlinson
ID: 17842258
Just google for:

    vb.net thread marshal delegate invoke

or:
   
    vb.net multithreading gui
0

Featured Post

6 Surprising Benefits of Threat Intelligence

All sorts of threat intelligence is available on the web. Intelligence you can learn from, and use to anticipate and prepare for future attacks.

Join & Write a Comment

Introduction When many people think of the WebBrowser (http://msdn.microsoft.com/en-us/library/2te2y1x6%28v=VS.85%29.aspx) control, they immediately think of a control which allows the viewing and navigation of web pages. While this is true, it's a…
Parsing a CSV file is a task that we are confronted with regularly, and although there are a vast number of means to do this, as a newbie, the field can be confusing and the tools can seem complex. A simple solution to parsing a customized CSV fi…
Internet Business Fax to Email Made Easy - With eFax Corporate (http://www.enterprise.efax.com), you'll receive a dedicated online fax number, which is used the same way as a typical analog fax number. You'll receive secure faxes in your email, fr…
This video demonstrates how to create an example email signature rule for a department in a company using CodeTwo Exchange Rules. The signature will be inserted beneath users' latest emails in conversations and will be displayed in users' Sent Items…

707 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

16 Experts available now in Live!

Get 1:1 Help Now