Over a million developers have joined DZone.

Essential Silverlight and WPF Skills: The UI Thread, Dispatchers, Background Workers and Async Network Programming

·

I'm starting a occasional series of postings on top things every Silverlight and WPF developer should know. Originally, I was going to make this a top-10 style list, but I'll keep that for my presentations, and instead expand on it here, with one topic per post.

This one is about threading in Silverlight.

Ok, that subject sounds like a lot, but it's all pretty closely related.

Both Silverlight and WPF have the concept of a UI thread. Whenever you tie that thread up, you make your application unresponsive. In Silverlight, if you're on a page with other plug-ins, you make all the plug-ins unresponsive. (It's actually per-process, but most modern browsers separate processes by tabs)

Async Network Calls

In order to keep from tying up the UI thread, you have to perform long-running processes on another thread. Often, but not always, those processes are network calls. In Silverlight, all network calls are async with no provision to handle them any other way. This lets you off the hook threading-wise, but requires that you understand how to chain those calls, and how to deal with libraries that have IAsyncResult callbacks that return on different threads.

TestServiceClient client = new TestServiceClient();
client.DoSomethingSlowCompleted += (s, ea) =>
{
// do something with the results here
Debug.WriteLine(ea.Result);
};

client.DoSomethingSlowAsync();

Background Worker

For other processes, the BackgroundWorker is a great, simple, way to do some background work while reporting progress to the UI. It takes all the dispatching work away, so you can feel free to manipulate the UI from the ProgressChanged and RunWorkerCompleted events. Do not touch the UI from the DoWork event, as that is actually running on a separate thread.

private BackgroundWorker _worker = new BackgroundWorker();
private void RunLongProcess()
{
_worker.WorkerReportsProgress = true;

ProgressBar.Minimum = 0;
ProgressBar.Maximum = 100;

_worker.DoWork += (s, e) =>
{
for (int i = 0; i < 100; i++)
{
// simulate long-running work
System.Threading.Thread.Sleep(500);
((BackgroundWorker)s).ReportProgress(i+1);
}

};

_worker.ProgressChanged += (s, e) =>
{
// this is on the UI thread, so you can
// update UI from here.
ProgressBar.Value = e.ProgressPercentage;
};

_worker.RunWorkerCompleted += (s, e) =>
{
// clean up after your stuff, yes, you can touch UI here.
};

_worker.RunWorkerAsync();

}

Of course, knowing how to set up event handlers using lambda expressions is always helpful :)

Dispatcher

Sometimes, you spin off a thread of your own, or you have to deal with some of the crufty stacks where the callbacks come back on a different thread from the one that created the call. In those cases, you need to use a dispatcher to make a call back to the UI thread.

If the code is on a page, you can call the dispatcher property and pass it a function to execute (yes, more lambda in this example).

private void UpdateUI()
{
Dispatcher.BeginInvoke(() =>
{
ProgressBar.Value = 50;
});
}

But, if the code is in some sort of non-UI class, it gets more complex. You can do it this way, using a dummy control (like we did in Silverlight 2):

TextBlock _dispatcherObject = new TextBlock();
private void UpdateUINasty() // avoid this approach unless it's all you've got
{
_dispatcherObject.Dispatcher.BeginInvoke(() =>
{
SomePage.ProgressBar.Value = 50;
});
}

This example assumes the object itself was created on the UI thread. The better way is to use the Deployment object:

private void UpdateUINotSoNasty()
{
Deployment.Current.Dispatcher.BeginInvoke(() =>
{
SomePage.ProgressBar.Value = 50;
});
}

That's better. Definitely the recommended approach for recent versions of Silverlight, including Windows Phone 7.

Timers

What about when you need to run something on a timer? The normal timer classes require manual dispatching to do any UI updates. However, Silverlight and WPF both have the DispatcherTimer class that handles all that for you.

private void StartTimer()
{
DispatcherTimer timer = new DispatcherTimer();

timer.Tick += (s, e) =>
{
// do some very quick work here

// update the UI
StatusText.Text = DateTime.Now.Second.ToString();
};

timer.Interval = TimeSpan.FromSeconds(1);
timer.Start();
}

Of course, realize that what you're doing here is interrupting the UI thread, not really running anything on a separate thread. It's not suitable for anything long-running and cpu-intensive, but rather something where you need to execute on a regular interval. Clock UI updates are a perfect example.

Threads

In general, I discourage folks from spinning off real threads. Very few people understand how to efficiently work with threads, so even if you're a threading genius, the person coming in after you probably isn't. In most cases, the Background Worker will provide you with what you need.

Silverlight doesn't yet have it, but the Parallel Tasking support built into .NET 4 can really help you out should you really need to do more than two things at once. Definitely check it out.

Conclusion

Threading in Silverlight and WPF is a little trickier than some other technologies, because you have to worry about the UI thread. My intention here was to both inform you that this trickiness exists, and then show some ways of dealing with it.

Topics:

Published at DZone with permission of Pete Brown. See the original article here.

Opinions expressed by DZone contributors are their own.

The best of DZone straight to your inbox.

SEE AN EXAMPLE
Please provide a valid email address.

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.
Subscribe

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}