Results 1 to 8 of 8

Thread: [2005] Threading problem(adding to listbox?)

  1. #1

    Thread Starter
    Frenzied Member
    Join Date
    Sep 2005
    Posts
    1,547

    [2005] Threading problem(adding to listbox?)

    Well when I try to do listbox1.items.add() in a threading.thread it errors with "Cross-thread operation not valid: Control 'listBox1' accessed from a thread other than the thread it was created on.". Is there a way around this so I can add to the listbox in the thread?

  2. #2
    Member
    Join Date
    Apr 2007
    Posts
    36

    Re: [2005] Threading problem(adding to listbox?)


  3. #3

    Thread Starter
    Frenzied Member
    Join Date
    Sep 2005
    Posts
    1,547

    Re: [2005] Threading problem(adding to listbox?)

    Quote Originally Posted by gaccettola
    Is there an easier way to do it?

  4. #4
    Member
    Join Date
    Apr 2007
    Posts
    36

    Re: [2005] Threading problem(adding to listbox?)

    by the book you don't touch the ui except from the ui's thread, there are two (ok, probably more than two - but two that keep coming up to answer this question) options: invoke and ... and the one I can't remember but don't use (also can't remember why I didn't like the other one)

  5. #5
    Super Moderator jmcilhinney's Avatar
    Join Date
    May 2005
    Location
    Sydney, Australia
    Posts
    110,299

    Re: [2005] Threading problem(adding to listbox?)

    There's nothing hard about delegation. It is the ONLY proper way to access control members from a worker thread. You can set Control.CheckForIllegalCrossThreadCalls to False but that is a hack that leaves your application open to potential errors.

    If you don't want to use explicit delegation then you can use a BackgroundWorker to create your worker thread implicitly. The DoWork event is raised in a worker thread. You can then call the ReportProgress method to raise the ProgressChanged event in the UI thread. That means that you can access controls in the ProgressChanged event handler directly because the BGW handles the delegation implicitly.

    If you don't want to use a BackgroundWorker then, as I said, there is nothing hard about delegation. You simply write a method to perform the control access:
    vb Code:
    1. Private Sub AddListBoxItem(ByVal item As Object)
    2.     Me.ListBox1.Items.Add(item)
    3. End Sub
    Then you declare a delegate with the same signature, i.e. argument list and return type:
    vb Code:
    1. Private Delegate Sub AddListBoxItemInvoker(ByVal item As Object)
    2.  
    3. Private Sub AddListBoxItem(ByVal item As Object)
    4.     Me.ListBox1.Items.Add(item)
    5. End Sub
    Now you add an If block to the method to check which thread your on and, if you're on a worker thread, create a delegate:
    vb Code:
    1. Private Delegate Sub AddListBoxItemInvoker(ByVal item As Object)
    2.  
    3. Private Sub AddListBoxItem(ByVal item As Object)
    4.     If Me.ListBox1.InvokeRequired Then
    5.         'We are on a worker thread so marshal the call to the UI thread.
    6.         Me.ListBox1.Invoke(New AddListBoxItemInvoker(AddressOf AddListBoxItem), _
    7.                            item)
    8.     Else
    9.         'We are on the UI thread so access the control directly.
    10.         Me.ListBox1.Items.Add(item)
    11.     End If
    12. End Sub
    Notice the similarity between the last two code snippets. What ever was the original contents of the method becomes the contents of the Else block. The bit you add for the delegation is just a few lines and is always the same pattern:

    1. Use the InvokeRequired property of the control you want to access to determine which thread you're on.
    2. If you're on a worker thread then call the control's Invoke method.
    3. The first argument of Invoke is a delegate with the same signature as the current method. You pass the address of the current method to the delegate constructor.
    4. The rest of the arguments of Invoke are the very same arguments of the current method.

    That's all there is to it. If you understand what's happening it's extremely simple to replicate that pattern over and over again for all sorts of situations. Basically, what the Control.Invoke method does is take a delegate, move it across the thread boundary to the thread that owns the control, then call the delegtes Invoke method. A delegate is an object that contains a reference to a method and its Invoke method will execute that method. That means that this whole code pattern simply creates an object with a reference to the current method and sends that object the the UI thread, where it then re-executes the same method.

    Note also that if your method has no arguments and no return value then you don't have to declare your own delegate. You can simply use the existing MethodInvoker delegate.

  6. #6

    Thread Starter
    Frenzied Member
    Join Date
    Sep 2005
    Posts
    1,547

    Re: [2005] Threading problem(adding to listbox?)

    Quote Originally Posted by jmcilhinney
    There's nothing hard about delegation. It is the ONLY proper way to access control members from a worker thread. You can set Control.CheckForIllegalCrossThreadCalls to False but that is a hack that leaves your application open to potential errors.

    If you don't want to use explicit delegation then you can use a BackgroundWorker to create your worker thread implicitly. The DoWork event is raised in a worker thread. You can then call the ReportProgress method to raise the ProgressChanged event in the UI thread. That means that you can access controls in the ProgressChanged event handler directly because the BGW handles the delegation implicitly.

    If you don't want to use a BackgroundWorker then, as I said, there is nothing hard about delegation. You simply write a method to perform the control access:
    vb Code:
    1. Private Sub AddListBoxItem(ByVal item As Object)
    2.     Me.ListBox1.Items.Add(item)
    3. End Sub
    Then you declare a delegate with the same signature, i.e. argument list and return type:
    vb Code:
    1. Private Delegate Sub AddListBoxItemInvoker(ByVal item As Object)
    2.  
    3. Private Sub AddListBoxItem(ByVal item As Object)
    4.     Me.ListBox1.Items.Add(item)
    5. End Sub
    Now you add an If block to the method to check which thread your on and, if you're on a worker thread, create a delegate:
    vb Code:
    1. Private Delegate Sub AddListBoxItemInvoker(ByVal item As Object)
    2.  
    3. Private Sub AddListBoxItem(ByVal item As Object)
    4.     If Me.ListBox1.InvokeRequired Then
    5.         'We are on a worker thread so marshal the call to the UI thread.
    6.         Me.ListBox1.Invoke(New AddListBoxItemInvoker(AddressOf AddListBoxItem), _
    7.                            item)
    8.     Else
    9.         'We are on the UI thread so access the control directly.
    10.         Me.ListBox1.Items.Add(item)
    11.     End If
    12. End Sub
    Notice the similarity between the last two code snippets. What ever was the original contents of the method becomes the contents of the Else block. The bit you add for the delegation is just a few lines and is always the same pattern:

    1. Use the InvokeRequired property of the control you want to access to determine which thread you're on.
    2. If you're on a worker thread then call the control's Invoke method.
    3. The first argument of Invoke is a delegate with the same signature as the current method. You pass the address of the current method to the delegate constructor.
    4. The rest of the arguments of Invoke are the very same arguments of the current method.

    That's all there is to it. If you understand what's happening it's extremely simple to replicate that pattern over and over again for all sorts of situations. Basically, what the Control.Invoke method does is take a delegate, move it across the thread boundary to the thread that owns the control, then call the delegtes Invoke method. A delegate is an object that contains a reference to a method and its Invoke method will execute that method. That means that this whole code pattern simply creates an object with a reference to the current method and sends that object the the UI thread, where it then re-executes the same method.

    Note also that if your method has no arguments and no return value then you don't have to declare your own delegate. You can simply use the existing MethodInvoker delegate.
    That works but now my thread is supposed to loop repeatedly but for some reason doesn't.

  7. #7
    Super Moderator jmcilhinney's Avatar
    Join Date
    May 2005
    Location
    Sydney, Australia
    Posts
    110,299

    Re: [2005] Threading problem(adding to listbox?)

    I'm getting nothing via ESP. Must be interference from my wireless network. I'm afraid you'll have to post the code.

  8. #8
    New Member
    Join Date
    Feb 2010
    Posts
    8

    Thumbs up Re: [2005] Threading problem(adding to listbox?)

    Thank You Very Much... It was very useful.....


    Quote Originally Posted by jmcilhinney View Post
    There's nothing hard about delegation. It is the ONLY proper way to access control members from a worker thread. You can set Control.CheckForIllegalCrossThreadCalls to False but that is a hack that leaves your application open to potential errors.

    If you don't want to use explicit delegation then you can use a BackgroundWorker to create your worker thread implicitly. The DoWork event is raised in a worker thread. You can then call the ReportProgress method to raise the ProgressChanged event in the UI thread. That means that you can access controls in the ProgressChanged event handler directly because the BGW handles the delegation implicitly.

    If you don't want to use a BackgroundWorker then, as I said, there is nothing hard about delegation. You simply write a method to perform the control access:
    vb Code:
    1. Private Sub AddListBoxItem(ByVal item As Object)
    2.     Me.ListBox1.Items.Add(item)
    3. End Sub
    Then you declare a delegate with the same signature, i.e. argument list and return type:
    vb Code:
    1. Private Delegate Sub AddListBoxItemInvoker(ByVal item As Object)
    2.  
    3. Private Sub AddListBoxItem(ByVal item As Object)
    4.     Me.ListBox1.Items.Add(item)
    5. End Sub
    Now you add an If block to the method to check which thread your on and, if you're on a worker thread, create a delegate:
    vb Code:
    1. Private Delegate Sub AddListBoxItemInvoker(ByVal item As Object)
    2.  
    3. Private Sub AddListBoxItem(ByVal item As Object)
    4.     If Me.ListBox1.InvokeRequired Then
    5.         'We are on a worker thread so marshal the call to the UI thread.
    6.         Me.ListBox1.Invoke(New AddListBoxItemInvoker(AddressOf AddListBoxItem), _
    7.                            item)
    8.     Else
    9.         'We are on the UI thread so access the control directly.
    10.         Me.ListBox1.Items.Add(item)
    11.     End If
    12. End Sub
    Notice the similarity between the last two code snippets. What ever was the original contents of the method becomes the contents of the Else block. The bit you add for the delegation is just a few lines and is always the same pattern:

    1. Use the InvokeRequired property of the control you want to access to determine which thread you're on.
    2. If you're on a worker thread then call the control's Invoke method.
    3. The first argument of Invoke is a delegate with the same signature as the current method. You pass the address of the current method to the delegate constructor.
    4. The rest of the arguments of Invoke are the very same arguments of the current method.

    That's all there is to it. If you understand what's happening it's extremely simple to replicate that pattern over and over again for all sorts of situations. Basically, what the Control.Invoke method does is take a delegate, move it across the thread boundary to the thread that owns the control, then call the delegtes Invoke method. A delegate is an object that contains a reference to a method and its Invoke method will execute that method. That means that this whole code pattern simply creates an object with a reference to the current method and sends that object the the UI thread, where it then re-executes the same method.

    Note also that if your method has no arguments and no return value then you don't have to declare your own delegate. You can simply use the existing MethodInvoker delegate.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  



Click Here to Expand Forum to Full Width