How To Handle Cross-thread Access To GUI Elements

When creating applications in managed .NET, developers should be very careful when changing the UI elements (e.g., all the components on your form) from within working threads. According to MSDN documentation this can cause in unpredictable behaviour of your GUI elements. This brief "how-to" will explain how to handle this problem.

The cross thread UI changes are handled differently in the .NET versions:

VisualStudio 2003 (.NET 1.1) simply ignores this fact, and doesn't check for cross-thread UI changes.
VisualStudio 2005 (.NET 2.0) recognizes the attempts when trying to change the UI from the thread, and throws an Exception. To avoid the check for these so called "illegal cross thread calls" you can set the CheckForIllegalCrossThreadCalls flag in your constructor to false:

Form1(){
  Control.CheckForIllegalCrossThreadCalls = false;
  Initialize();
}

However, this only avoids the exception; it still can result in UI errors.

Using Delegates and Invoke to make changes with the main UI thread

You can use the Invoke command in your code to call a Delegate method to perform the changes on the UI. These changes are then executed by the main UI thread. Here are three examples how to use these delegates.

Calling Delegates w/o parameters

private delegate void AttachExecuteDelegate();
private void AttachExecute()
{
  // here we can modify all the GUI elements; we’re now in the
  // main thread of the application
}

// This is the ‘normal’ callback method.
// If we change GUI elements inside of this method, we will see the exception message...
private void ik_InputChange(object sender, BitStateChangeEventArgs e)
{
  // ...therefore we call the delegate method with the INVOKE command
  this.Invoke(new AttachExecuteDelegate(AttachExecute));
}

Calling Delegates w/ parameters

private delegate void InputChangeExecuteDelegate(BitStateChangeEventArgs e);
private void InputChangeExecute(BitStateChangeEventArgs e)
{
  // here we can modify all the GUI elements; we’re now in the
  // main thread of the application
}

// This is the ‘normal’ callback method.
// If we change GUI elements inside of this method, we will see the exception message...
private void ik_InputChange(object sender, BitStateChangeEventArgs e)
{
  // ...therefore we call the delegate method with the INVOKE command
  this.Invoke(new InputChangeExecuteDelegate(InputChangeExecute), new object[] { e });
}

And here is an example how to handle incoming notifications from the shared dictionary

private delegate void SubscriptionNotifiedDelegate(SubscriptionEventArgs e);
private void SubscriptionNotified(SubscriptionEventArgs e)
{
  // here we can modify all the GUI elements; we’re now in the
  // main thread of the application
}

// This is the callback method from the .Networking subscription object.
// If we change GUI elements inside of this method, we will see the exception message...
void subscription_Notified(object sender, SubscriptionEventArgs e)
{
  this.Invoke(new SubscriptionNotifiedDelegate(SubscriptionNotified), new object[] { e });
}

In Line delegates

These are another way of doing things. Here are three ways.

Version 1. (Uses parameters


//
private delegate void ButtonChangeContentsDelegate(Button button, string text);
...
void SomeNonUIThreadMethod(Button btn, string str)
{
            this.Dispatcher.BeginInvoke(System.Windows.Threading.DispatcherPriority.Normal,
            (ButtonChangeContentsDelegate)delegate(Button button, string text)
            { button.Content = text; }, btn, str);
}
 

Version 2. No parameters required

using System.Windows.Threading;
public delegate void UIThreadDelegate();
private readonly Dispatcher uiThreadDispatcher = Dispatcher.CurrentDispatcher;
void SomeNonUIThreadMethod(Button btn, string str)
{
            UIThreadDelegate aDelegate = delegate
            {
                btn.Content = text;
            }; uiThreadDispatcher.BeginInvoke(aDelegate, DispatcherPriority.Background);
}

Version 3.

this.Dispatcher.Invoke(System.Windows.Threading.DispatcherPriority.Normal, new Action(delegate() {
      xxxxxxxx    insert the code here   xxxxxxxx
}));

However, there are a few things you need to know abou this approach. First, if you want to use this in a loop, make sure you make a copy of any value you want to use inside the pair of braces. For example,

for(int i = 0, i < max; i++) {
    noParaDelegate aDelegate = delegate
    {
        var j = i;
  do something with j rather than i, because this inline delegate will point to the address of i which is overwritten for each iteration. "i" will only be evaluated the updated value.
    };
    uiThreadDispatcher.BeginInvoke(aDelegate,DispatcherPriority.Background);
}

The same applies for the situation where you get a rash of callbacks.

callback(...) {
   noParaDelegate aDelegate = delegate
   {
        var j = e.value as var;
  do something with j rather than e.value, because e.value is overwritten for each callback and the Dispatcher Queue will always refer to the updated value.
   }; uiThreadDispatcher.BeginInvoke(aDelegate, DispatcherPriority.Background);
}

The reason a delegate refers to an address is that technically you should rather use a delegate with parameters and a copy of the parameters are made when the inline is prepared at run-time. The noParaDelegate takes no value from outside. Var j = i explicitly makes a copy when the inline is prepared at run-time. This doesn't conform to professional coding standard but declaring a delegate with different number of parameters is a pain. It seems the callbacks from a single sd are always synchronized, so the noParaDelegate never produces a problem for me. But in case the callbacks are not synced, you should always use delegate with actual parameters to pass value since an inline may not be prepared properly before another inline is prepared.