Introduction
WPF is a powerful tool for developing desktop applications. While developing apps using WPF, many times a situation comes to update UI thread from another background thread or some other thread. This tip explains how to update WPF thread from another thread in a safe way.
Background
I hope you are at least aware of the basics of threading concepts. If not, then no worries. I wil explain here some of the basics.
"Thread is the mother of any process". Remember always, first thread comes and then it takes the shape of process. All threads won't become processes. However, each process must have thread. A process once come in live, i.e., when process is running in OS environment, then it has the capability to request for more OS threads just to delegate tasks and make its own life easy.
Now consider this process is WPF application running on OS. Each process has a thread associated with it, so does with WPF application which we call it as main thread, i.e., Dispatcher thread. All messaging, updating controls, commands, etc. will be taken care of through this main thread (Dispatcher thread). If we try to update any control apart from this main thread, WPF actively refuses and hence we get an exception saying, "This type of CollectionView
does not support changes to its SourceCollection
from a thread different from the Dispatcher
thread.".
There is access check making another thread fault for UI control update.
So what is the solution for this situation? It's simple, borrow UI thread context and update control through this context from any thread. Remember, we borrowed UI thread context so update will be done through UI thread only even call from another thread to do so.
Let's see first what is a problematic situation with example and then modify the code making the situation better.
Using the Code
Following is a sample code which updates listbox
UI control from viewmodel
in another thread apart from dispatcher
thread:
private void BeginWork()
{
Task.Factory.StartNew(() =>
{
IsWorkDone = false;
var random = new Random();
for (int number = 1; number < 100; number++ )
{
Numbers.Add(random.Next(100));
Thread.Sleep(1000);
}
IsWorkDone = true;
});
}
Here, Numbers
is an observable collection. This collection we want to update at runtime in another thread. As observable collection is binded to ListBox
as collection source, we can't update data from another thread. To correct this problem, we will use UI's view context.
Using View Context
View context is nothing but thread synchronization context from main thread. This can be obtained from SynchronizationContext.Current
under System.Threading
. Following is the modified code using view context:
private void BeginWork()
{
Numbers.Clear();
Task.Factory.StartNew(() =>
{
IsWorkDone = false;
var random = new Random();
for (int number = 0; number < 5; number++)
{
ViewContext.Send(x => Numbers.Add
(random.Next(100)), null);
Thread.Sleep(1000);
}
ViewContext.Send(x => { NumberSelected = Numbers[0]; IsWorkDone = true; }, null);
});
}
In ViewModel
, we keep a ViewContext
property of type SynchronizationContext
. In code behind, we assign value for this property like:
Quote:
((MainWindowViewModel)DataContext).ViewContext = SynchronizationContext.Current;
Points of Interest
This is the most simplest and safest way to update in UI thread context.