Executing Code on the UI Thread in Silverlight 2

Silverlight Logo

With the asynchronicity question still dogging Silverlight 2, I thought I'd mention an oft forgotten little class in Silverlight (and in WPF) called the Dispatcher. Much of the confusion with asynchronous programming seems to stem from the fact that developers over complicate the problem.  Think that they need to handle the cross thread calls themselves. They tend to create two way communications for this or overuse the BackgroundWorker's ReportProgress functionality.

The key to simplifying calling the UI thread is the Dispatcher class.  This class supports a static (or shared) interface for executing code on the UI thread.  For example, you can call Dispatcher.BeginInvoke to invoke some arbitrary code on the UI thread:

// With Simple Lambda
Dispatcher.BeginInvoke(() => DoSomething());

// Also With Lambda
Dispatcher.BeginInvoke(() =>
  {
    DoSomething();
  }
  );

// or with Anonymous Delegate
Dispatcher.BeginInvoke(delegate()
  {
    DoSomething();
  } );

This is all is required to execute code on the UI thread.  The Dispatcher guarantees that this code is executed on the main UI thread. This is simplified versus the same WPF code. The Windows Presentation Foundation  uses a a prioritized message queue so that the Dispatcher really allows developers to not only make calls to the UI thread, but do so with some priority attached.  (See my MSDN article on the Dispatcher in WPF for a more detailed explanation). In Silverlight 2, the model is simplified (for better or worse). When calling the UI thread, you simply need to specify the code to call on the UI thread...the priority is gone.

Unfortunately there is no way currently to reliably test for the UI thread (again unlike in WPF where the DispatcherObject was part of the object hierarchy so you could call CheckAccess to see if you were on the UI thread). Because of this Silverlight 2 has a fundamental problem. It is unclear when we are on the UI thread. This is especially problematic for the event driven architecture that Silverlight 2 employs. This is exemplified by the fact that during some events (WebClient calls are especially prone to this behavior) seem to fail silently when you try and update the UI.  This is unlike the BackgroundWorker who throws an UnauthorizedAccessException (with the message of Invalid Cross Thread Access). So at times I find myself throwing in Dispatcher.BeginInvoke calls if the UI is failing to update in events (both control events and other events). This isn't a great solution but it does solve the issue much of the time.

I've attached a simple Silverlight 2 sample that shows this technique.  Let me know if you have any questions about this sample.

Comments:

Gravatar

CheckAccess() is available on the objects, it is just hidden in the IDE by an attribute. See my post at http://www.cameronalbert.com/post/2008/03/Multi-threading-in-Silverlight.aspx.

Gravatar

Thanks for the post.
I tried your example and it threw an UnauthorizedAccessException on the first DoSomething call in worker_DoWork. Was that intentional to show the BackgroundWorker thread can't access the UI?
cheers,
Stephen

Gravatar

Yes, the first call to the worker_DoWork was to show that it didn't work. It was supposed to do what you saw.

Gravatar

Thanks a lot :-) You made my day.
I used the Invoke method in Winform controls, and I did not has a clue about the Dispatcher features.

Gravatar

I have a scenario where i wish to read some xml files from the SL Hosting web project. In App.xaml i used the WebClient object for reading the same and then using the app object i read the values in page xaml file. Now if i use the Dispatcher and thread in App.xaml.cs file and read the data from xml files and then call this.RootVisual = new Page(); in App.xaml.cs filei get an "Invalid cross-thread access" in the exception block in the app.xaml.cs file. My approach used to work in Beta 1 but now it is failing. Can you please help me in this regard.
Thanks in Advance,
Parimal

Gravatar

You have to use the Dispatcher to do the actual call to "New Page()". Using it to do the WebClient stuff is completely unnecessary. I think you have the pattern backwards.

Gravatar

Thanks for the response. I have changed the code as said by you in App.xaml.cs file still i get the same error. My code is:
public App()
{
this.Startup += this.Application_Startup;
this.Exit += this.Application_Exit;
this.UnhandledException += this.Application_UnhandledException;
ThreadStart start = new ThreadStart(NonUIWork);
Thread thread = new Thread(start);
thread.Start();
InitializeComponent();
}
#endregion

void NonUIWork()
{
this.RootVisual.Dispatcher.BeginInvoke(() =>
{
DoSomething();
}
);

}

void DoSomething()
{
this.RootVisual = new Page();
}

#region Event - Application_Startup
/// <summary>
///
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void Application_Startup(object sender, StartupEventArgs e)
{
HttpWebRequest xmlCommonData = (HttpWebRequest)WebRequest.Create(new Uri(GetAppPath() + "XML/Common.xml"));
xmlCommonData.BeginGetResponse(new AsyncCallback(CommonDataResponseCallBack), xmlCommonData);


}
#endregion

Gravatar

While this shold work, I can't tell from the code exactly what is happening... I would post this on the forums on Silverlight.net. Also, as a general Rule I would do this in the startup of your Page.xaml, not in the App.xaml.cs.

Gravatar

Thanks for the quick response.
I tried this approach in Page but then page files get loaded first and then xml is read through the thread which breaks my app.

can you give me some sample.
regards,
parimal

Gravatar

Sorry, but I can't put together an example. Again, the best approach is to use the power of the community on Silverlight.net and go ask this question on the forums at http://silverlight.net/forums


 



 
Save Cancel