The Moth - Invoke CF and Full Fx (original) (raw)

« VB6 -> C# | Invoke with Whidbey »

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

    mDataQue.Enqueue(o)

End SyncLock


' assuming all this code is in a form

Me.Invoke(mUpdateDel)

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.