Accessing WPF controls on a non UI Thread


I am sure most of you would have run into this issue before. When you try to access a WPF control on a different thread other than your regular UI thread you get a runtime exception “The calling thread cannot access this object because a different thread owns it.”. To understand why this happens, it is important to know that WPF applications run on two different threads – one for rendering the controls and the other to manage the user interface. The rendering thread runs in the background and the thread which is visible for us to work on is the UI thread. And hence most of the objects are tied to the UI thread and this is known as thread affinity. Thread affinity is handled by the Dispatcher. Each user interface related work item is channeled through the Dispatcher class, that means every work item is queued by the UI thread in an object called Dispatcher. Dispatcher runs the work items on priority basis. Supporting work item prioritization allow WPF to work on a an item for more time and hence more time is consumed on the UI thread. UI thread will have at least one Dispatcher, and each Dispatcher can execute work items in exactly one thread. So it is important to release the work item from the thread as fast as possible so as to increase the UI responsiveness.  To keep the work item small,  I spawned newer threads and executed the discrete blocks of code on these threads. This is exactly where I ran into the exception stated above (when I tried to access the  WPF control).

So here is how I got the exception:

private void myButton_Click(object sender, RoutedEventArgs e)
{
    Thread t = new Thread(new ThreadStart(
        delegate
        {
            //accessing the TextBlock.Text on a different thread – **incorrect**
            myText.Text = "HI";
        }
        ));
    t.Start();
}

 

So how to get rid of this?

Now we know why a background running thread cannot access the UI thread. To get rid of this problem it is pretty simple – “Just ask the UI thread to do it for you”. Yes the background thread can request the UI thread to update the control properties on behalf of it. This is achieved by calling the Invoke or the BeginInvoke method of the DispatcherObject class. This will register the work items to the Dispatcher. Invoke is a synchronous call – that is, it doesn’t return until the UI thread actually finishes executing the delegate. BeginInvoke is asynchronous and returns immediately. So as stated above Dispatcher orders work items by priority and hence you can pass the priority using DispatcherPriority enumerator while registering the work item to Dispatcher. 
So here is the code below which solved my problem:

private void myButton_Click(object sender, RoutedEventArgs e)
{
    Thread t = new Thread(new ThreadStart(
        delegate
        {
            Dispatcher.Invoke(DispatcherPriority.Normal, new Action<TextBlock>(SetValue), myText);
        }
        ));
    t.Start();
}

private static void SetValue(TextBlock txt)
{
    txt.Text = "HI";
}

In the Dispatcher.Invoke() method used above takes three parameters – DispatcherPriority Enum, Delegate to execute the code and the parameter object. I used the Action<T> generic type to pass the control to the method to set its property.

It is suggested to use the setting of the control properties in the UI thread itself, and only move those code like say calculations to another thread. This will provide a better code maintenance.

However in the above example we do not improve the UI responsiveness, as the Invoke() method is called synchronously. Instead use BeginInvoke() method. My intention was to give an insight of the exception – why, and how to deal with it. If you need details on writing better responsive applications refer Shawn Wildermuth’s article

Wow another weekend in research -Cheers!

About these ads

2 thoughts on “Accessing WPF controls on a non UI Thread

  1. Oh my goodness! Awesome article dude! Many thanks, However I am
    experiencing troubles with your RSS. I don’t understand why I cannot subscribe to it. Is there anybody else getting identical RSS problems? Anyone who knows the answer can you kindly respond? Thanx!!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s