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:
Private Sub AddListBoxItem(ByVal item As Object)
Me.ListBox1.Items.Add(item)
End Sub
Then you declare a delegate with the same signature, i.e. argument list and return type:
vb Code:
Private Delegate Sub AddListBoxItemInvoker(ByVal item As Object)
Private Sub AddListBoxItem(ByVal item As Object)
Me.ListBox1.Items.Add(item)
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:
Private Delegate Sub AddListBoxItemInvoker(ByVal item As Object)
Private Sub AddListBoxItem(ByVal item As Object)
If Me.ListBox1.InvokeRequired Then
'We are on a worker thread so marshal the call to the UI thread.
Me.ListBox1.Invoke(New AddListBoxItemInvoker(AddressOf AddListBoxItem), _
item)
Else
'We are on the UI thread so access the control directly.
Me.ListBox1.Items.Add(item)
End If
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.