One of the unique features that Silverlight brings to the browser platform is the ability for you to write multithreaded apps. You can use Thread.Start, ThreadPool.QueueUserWorkItem, and other mechanisms to execute code on another thread, and you can use the same thread-synchronization primitives that you’re already familiar with from the “big” .NET Framework to synchronize your threads’ actions.
One nuance you must be aware of, however, is that Silverlight distinguishes between UI threads and background threads, and only UI threads can access the XAML DOM. When you launch a thread with Thread.Start or borrow a thread from the CLR thread pool with ThreadPool.QueueUserWorkItem, you’re using background threads that can’t touch a XAML object without throwing an exception. Even if you’re not explicitly launching threads, classes in the framework may be doing it for you. If you update a TextBlock or a bind to a ListBox in a WebClient completion event handler, for example, the update works just fine because WebClient fires events on the application’s UI thread. But if you attempt the same in an HttpWebRequest completion method, you’ll throw an exception because HttpWebRequest calls completion methods on—you guessed it—background threads.
Most texts have you remedy this situation by declaring a delegate through which a method that accesses the XAML DOM can be invoked, and then using a Dispatcher object (accessed through the Dispatcher property that XAML objects inherit from DependencyObject) to marshal back to the UI thread:
// Declare a delegate
private delegate void UpdateUIDelegate(Object state);
// Call UpdateUI on the UI thread
Output.Dispatcher.BeginInvoke(new UpdateUIDelegate(UpdateUI), data);
private void UpdateUI(Object state)
{
// Update a XAML TextBlock
Output.Text = “Hello, Silverlight”;
}
This works, but it’s slightly unwieldy. That’s why when I need to marshal back to my application’s UI thread, I often use a DispatcherSynchronizationContext instead. You begin by caching a reference to the UI thread’s DispatcherSynchronizationContext in the page constructor. Then, anytime you’re running on a background thread and need to call a method on the UI thread, you call the synchronization context’s Post or Send method:
private SynchronizationContext _context;
public Page()
{
InitializeComponent();
_context = SynchronizationContext.Current;
}
// Call UpdateUI on the UI thread
_context.Post(UpdateUI, data);
This is slightly cleaner, and it offers the added option of making a synchronous call to UpdateUI (with DispatcherSynchronizationContext.Send) if circumstances require it.