WPF Dispatcher - Accessing UI Thread From Another Thread

If you're a C#.NET developer creating a WPF application and it's obvious that you could come across a scenarios to use multi threading.
Have you ever tried to access an element in the UI from another thread and come across an exception with the following message ?

"The calling thread cannot access this object because a different thread owns it."

What's the reason for such message ?
It's something like this. In certain programming languages the application starts with something called the main thread or the UI thread. And usually, the elements of the UI, are created with that main thread. When another thread tries to access it, that message is given.

Put into simple words, "When you're doing something to one of the UI elements, you have to do it through the UI thread."

The next question is,  how to do such thing ?
When you're working with WPF, every thread has an associated Dispatcher which provides services for managing the queue of work items for a thread.
And you can use this Dispatcher to access the UI thread and do modifications to the elements in the UI from another thread.

I'll provide couple of approaches to do this, not so different from each other.

1. First one is; when you take an UI element, let's say a Window or a UserControl, these have an attribute of them called Dispatcher. So inside the code behind file of that class, you can access it as an instance variable. But you have to make sure, you're trying to do this within the class as it's an attribute of that class.
(If you have the reference to such UI element in another class, you can still access the Dispatcher as it's a public attribute)

After accessing this attribute, you can simply access the UI thread and do modifications using the following code segment.

Synchronous
Dispatcher.Invoke((Action)(() =>
{
    //Write the modifications to
    //UI you want to do.
}));
Asynchronous

Dispatcher.BeginInvoke((Action)(() =>
{
     //Write the modifications to
     //UI you want to do.
}));

You have to make sure is, it's the Dispatcher of the UI or the Main Thread. Because other non UI threads also may have their own Dispatchers.

2. The second approach or the other code segment I'm providing you that doesn't require to do this using the attribute 'Dispatcher' in the UI element. So it's sounds like less pain (Still I prefer the 1st approach for a cleaner code ;) ).
Using the 'Applcation' object of 'System.Windows' namespace we can do the same thing, more easily in a straight forward way. So in that case it'll be,

Synchronous

Application.Current.Dispatcher.Invoke((Action)(() =>
{
  //Write the modifications to
 //UI you want to do.
}));

Asynchronous

Application.Current.Dispatcher.BeginInvoke((Action)(() =>
{
   //Write the modifications to
  //UI you want to do.
}));

In any scenario of these, if comes a situation where you don't know from which thread (UI or some other thread), a block of code is going to be executed, there's a solution for that too. But this is very unlikely with my own experience and with what I've read about. Because most of the times we know what is the thread that is going to execute that block of code.
In my case, most of the time I come across this scenario, when I don't want to duplicate some code and want to use the same method in two places which are executed by two different threads.

Another thing I forgot to mention is, any UI interaction from the user (like clicking on button) is execute on the UI thread. So for them, you don't have to worry about the Dispatchers.

So when you don't know whether that block of code is execute in the UI thread or some other thread, you can use the CheckAccess() method of the Dispatcher object.

If it returns true, it means it's executed in the UI thread and, that execution has the privilege to modify the UI.
If it returns false, it means it's executed in some other thread and, that execution doesn't have the privilege to modify the UI, where you have to use the Dispatcher to access the UI thread.
In a simple block of code it's like,

if (!Dispatcher.CheckAccess())
{
    Dispatcher.BeginInvoke((Action)(() =>
    {
       //Write the modifications to
       //UI you want to do.
    }));               
} 
else
{
    //Write the modifications to
    //UI you want to do.
}
The CheckAccess() method is not shown by the Intellisense. This discussion explains why. Do don't panic if it doesn't come when you press CTRL + SPACE. Just write the method with correct spelling and build your code. It will work perfectly.

Please refer the following link for a code sample.
https://github.com/nohimys/wpf-dispatcher-example

Good Luck :)

Comments

Popular posts from this blog

Downgrade NuGet Packages - Visual Studio

Callbacks With Interfaces Or Delegates