Solved

Cross-thread problem with Delegate

Posted on 2006-10-27
9
488 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
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 5
  • 4
9 Comments
 
LVL 86

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 86

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
Online Training Solution

Drastically shorten your training time with WalkMe's advanced online training solution that Guides your trainees to action. Forget about retraining and skyrocket knowledge retention rates.

 

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
 
LVL 86

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 86

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 86

Expert Comment

by:Mike Tomlinson
ID: 17842258
Just google for:

    vb.net thread marshal delegate invoke

or:
   
    vb.net multithreading gui
0

Featured Post

On Demand Webinar: Networking for the Cloud Era

Did you know SD-WANs can improve network connectivity? Check out this webinar to learn how an SD-WAN simplified, one-click tool can help you migrate and manage data in the cloud.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

This tutorial demonstrates one way to create an application that runs without any Forms but still has a GUI presence via an Icon in the System Tray. The magic lies in Inheriting from the ApplicationContext Class and passing that to Application.Ru…
Since .Net 2.0, Visual Basic has made it easy to create a splash screen and set it via the "Splash Screen" drop down in the Project Properties.  A splash screen set in this manner is automatically created, displayed and closed by the framework itsel…
In this video we outline the Physical Segments view of NetCrunch network monitor. By following this brief how-to video, you will be able to learn how NetCrunch visualizes your network, how granular is the information collected, as well as where to f…
If you’ve ever visited a web page and noticed a cool font that you really liked the look of, but couldn’t figure out which font it was so that you could use it for your own work, then this video is for you! In this Micro Tutorial, you'll learn yo…

719 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