This article is compatible with the latest version of Silverlight.
I've been playing with some timers and Web Services and I got stuck in a case where the background thread was trying to update the UI. Well, that's never going to happen. To update the UI you should use the UI thread. Let's see how we can call the UI thread from the background thread.
Consider this situation:
You have data that needs to be updated every minute. The data is loaded by calling a web service.
For the update you can use the Timer class. Both the timer and the web service calls are executed asynchronously.
...
Timer t = new Timer( GetData, null, TimeSpan.Zero, new TimeSpan( 0, 1, 0 ) );
...
public void GetData( object stateInfo )
{
SampleWebServiceSoapClient client = new SampleWebServiceSoapClient();
client.HelloWorldCompleted +=
new EventHandler<HelloWorldCompletedEventArgs>( client_HelloWorldCompleted );
client.HelloWorldAsync();
}
private void client_HelloWorldCompleted( object sender, HelloWorldCompletedEventArgs e )
{
// update the UI
}
Unfortunately this code doesn't work. If you try to update the UI in the callback you will get an error like "Invalid cross-thread access".
So what we should do to make this code work? At least two ways to go here:
1. Use the Dispatcher class to call the UI thread
2. Instead of using System.Threading.Timer use the DispatcherTimer
Dispatcher class
The Dispatcher class greatly simplifies calling the UI thread with a static method BeginInvoke:
Dispatcher.BeginInvoke( Action );
This way, with just a line of code, you can update the UI. Let's consider a TextBlock.Text property that we want to be updated in the client_HelloWorldCompleted callback:
private void client_HelloWorldCompleted( object sender, HelloWorldCompletedEventArgs e )
{
Dispatcher.BeginInvoke( () => lastUpdated.Text = DateTime.Now.ToLongTimeString() );
}
The Dispatcher class guarantees that this code will be executed on the UI thread. For more information about the Dispatcher read this MSDN article.
DispatcherTimer
The DispatcherTimer is a special timer integrated into the Dispatcher queue that is reevaluated on every Dispatcher loop. That makes it perfect for our case. Let's see how we can change the code so instead of using the Threading.Timer to use the DispatcherTimer.
...
DispatcherTimer t = new DispatcherTimer();
t.Interval = new TimeSpan( 0, 0, 1 );
t.Tick += new EventHandler( RefreshData );
t.Start();
...
private void RefreshData( object sender, EventArgs e )
{
SampleWebServiceSoapClient client = new SampleWebServiceSoapClient();
client.HelloWorldCompleted +=
new EventHandler<HelloWorldCompletedEventArgs>( client_HelloWorldCompleted );
client.HelloWorldAsync();
}
private void client_HelloWorldCompleted( object sender, HelloWorldCompletedEventArgs e )
{
lastUpdated.Text = DateTime.Now.ToLongTimeString();
}
The DispatcherTimer runs on the same thread as the Dispatcher and guarantees that this code will be executed on the UI thread.
Summary
Using the Dispatcher is an universal method to execute code on the UI thread. However sometimes, as in the shown example, we can simplify our code even more. So choose carefully when you encounter such situations.
Hope this helps.
References
Build More Responsive Apps With The Dispatcher
Dispatcher class on MSDN
DispatcherTimer class on MSDN