Invoke CF and Full Fx

Fri, October 1, 2004, 05:24 AM under dotNET
It should be a well known fact by now that, when in need to updade controls created on the GUI thread, you must use Control.Invoke (call the thread-safe static Invoke method on any control, e.g. the Form). It should be but, judging at how often this comes up in the newsgroups, it's not.

The issue and solution are the same for both full and compact frameworks; the difference is that, although the desktop seems to be forgiving (sometimes updating from non-GUI thread works), the CF certainly isn't: every time you try it, the result is an application that is hung/frozen/locked up. If you see this on your device, start looking for the said problem.

Of course, like almost every other area of the CF, Control.Invoke support is limited. It is limited in 3 areas:

1. You cannot pass any delegate you want to Invoke; rather, you can only use the EventHandler. So, if you were hoping on using the efficient MethodInvoker or one of your own, forget about it.

2. Invoke cannot be used to pass arguments. So, you can marshal control from a thread to the GUI thread, but you cannot pass any parameters in the same call.

3. BeginInvoke is not supported. So, you can only do it synchronously (your non-GUI thread does not return until the Invoked method itself completes).

A solution to the 2nd limitation is to write code like this:

Private mDataQue As New Queue
Private mUpdateDel As New EventHandler(AddressOf UpdateBox)

' This method runs on a non-GUI thread e.g. Threading.Timer
Friend Sub OnNonGuiThread(ByVal o As Object)
' if you have more than one argument
' create an object or structure
' that holds the data you want to pass to the GUI thread
SyncLock mDataQue.SyncRoot
End SyncLock

' assuming all this code is in a form
End Sub

' This method runs on GUI thread
Private Sub UpdateBox(ByVal sender As Object, ByVal e As EventArgs)
Dim o As Object
SyncLock mDataQue.SyncRoot
If mDataQue.Count > 0 Then
o = mDataQue.Dequeue()
End If
End SyncLock

' TODO use o
' cast o to your object/structure and
' use it to update the GUI
End Sub

The 3rd limitation can be overome simply by using the threadpool. Extend the previous solution with:

' NOW, *this* is the method that runs on non-GUI thread
' e.g. Threading.Timer
' The original method is now just a helper
Public Sub OnNonGuiThread2(ByVal o As Object)
ThreadPool.QueueUserWorkItem(AddressOf OnNonGuiThread,o)
End Sub

We look at what Whidbey brings to the table next time.