Background Threads in WPF

Anyone writing smart client applications will eventually run into a situation where some work must be performed asynchronously on a background thread. Long running computations, database access, web service calls and large file I/O are all good candidates. Any long-running operation must be performed on a background thread to avoid freezing the user interface. More often than not the UI state needs to change after the background operation completes. Perhaps the UI must show feedback as the operation executes – think progress bar. It would be great to update the UI right from the background thread, but WPF has strict rules concerning threading.

There’s one rule really, but it’s important and will drive how you design the background thread to UI conversation. WPF UI elements must be manipulated only on the UI thread. This rule pertains to instances of any class that inherits directly or indirectly from DispatcherObject, and this means all UI elements.

So you can’t set TextBox.Text or TreeViewItem.Header from any but the UI thread, is that it? Well, not exactly. Data binding complicates things. I’ve created a sample program where a ListBox is bound to an ObservableCollection. With this binding in place any changes to the collection indirectly manipulate the ListBox. Recall that ObservableCollection raises the CollectionChanged event whenever the items list changes. The ListBox, or more accurately a CollectionView that the framework inserts between the ListBox and bound collection, handles his event. CollectionChanged is raised synchronously on whatever thread modified the collection. When performed on any other than the UI thread a NotSupportedException is raised. You can see this in action by clicking on the button labeled “Kaboom!” in the sample program.

wpfthreads

Here’s the code that executes when “Kaboom!” is clicked.

private void Kaboom_Click(object sender, RoutedEventArgs e)
{
    ThreadPool.QueueUserWorkItem(new WaitCallback(KaboomThreadProc), this);
}
static void KaboomThreadProc(object sender)
{
    Window1 win = sender as Window1;
    string item = GetNextItem();
    // Sleep to simulate a long running process
    Thread.Sleep(1000);
    win.AddItem(item);
}

WPF provides the Dispatcher class to route work back to the UI thread. A Dispatcher object is created on the UI thread when the application starts up. Work can be sent to the UI thread by using this Dispatcher object and it’s Invoke or BeginInvoke methods. Invoke executes the work synchronously while BeginInvoke is asynchronous. This Dispatcher object is accessed in several ways. The static Dispatcher.CurrentDispatcher property will return it when called on the UI thread. Every DispatcherObject keeps a reference to the Dispatcher from the thread on which it was created. In other words every UI element has a Dispatcher property that returns the UI thread’s Dispatcher object. The current example used the Window’s Dispatcher in order to update the ObservableCollection on the UI thread. Click the button labeled “Dispatcher” to execute this code which updated the ListBox successfully.

private delegate void AddItemDelegate(string item);
private void AddItem(string item)
{
    _coll.Add(item);
}
 static void DispatcherThreadProc(object sender)
{
    Window1 win = sender as Window1;
    string item = GetNextItem();
    // Sleep to simulate a long running process
    Thread.Sleep(1000);
    win.Dispatcher.Invoke(DispatcherPriority.Normal,
        new AddItemDelegate(win.AddItem), 
        item);
}

Note that Dispatcher is a priority based queue of work items that get executed as the thread’s message pump processes Windows messages. Here the sample invokes a delegate on the UI thread at Normal priority, but other higher or lower priorities could have been used.

BackgroundWorker, a holdover from Windows Forms, may also be used as it fires its RunWorkerCompleted event on the UI thread. Perform some background work in the DoWorkEventHandler delegate. The DoWorkEventArgs parameter that the delegate receives contains Result property that should be set to the result of the background operation. The Result is passed to the RunWorkerCompletedEventHandler delegate so that it may be used on the UI thread to update the UI state. Click the button labeled “BG Worker” to update the ListBox using this method.

private void BGWorker_Click(object sender, RoutedEventArgs e)
{
    BackgroundWorker bgWorker = new BackgroundWorker();
    bgWorker.DoWork += new DoWorkEventHandler(BGWorker_DoWork);
    bgWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(BGWorker_RunWorkerCompleted);
    bgWorker.RunWorkerAsync(this);
}
static void BGWorker_DoWork(object sender, DoWorkEventArgs e)
{
    string item = GetNextItem();
    // Sleep to simulate a long running process
    Thread.Sleep(1000);
    e.Result = item;
}
void BGWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
    AddItem(e.Result as string);
}

Source code for this sample may be downloaded here.

About these ads

3 Responses to “Background Threads in WPF”


  1. 1 Kevin September 6, 2011 at 8:57 pm

    It’s a shame you haven’t received feedback on this on this article. It’s well articulated and helped me a bunch. Thanks!

  2. 2 vijis15VJ September 14, 2011 at 6:56 am

    Thanks, very concise and precise.


  1. 1 Asynchronous Web Service Invocation « Discovering .NET Trackback on January 22, 2009 at 2:15 pm

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




January 2009
M T W T F S S
« Dec   Feb »
 1234
567891011
12131415161718
19202122232425
262728293031  
I am a part of all that I have met;
Yet all exprience is an arch whitherthro'
Gleams that untravell'd world, whose margin fades
For ever and for ever when I move.
How dull it is to pause, to make an end,
To rust unburnish'd, not to shine in use!
Alfred, Lord Tennyson

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: