(X) Hide this
    • Login
    • Join
      • Generate New Image
        By clicking 'Register' you accept the terms of use .

Tip: Asynchronous Silverlight - Execute on the UI thread

(8 votes)
Emil Stoychev
>
Emil Stoychev
Joined Oct 15, 2007
Articles:   23
Comments:   98
More Articles
10 comments   /   posted on Jun 24, 2008
Categories:   Patterns and Practices , General

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


Subscribe

Comments

  • -_-

    RE: Tip: Asynchronous Silverlight - Execute on the UI thread


    posted by Dennis on Jun 29, 2008 08:25

    Hi,

    Do you also have an example on parametrized asynchronous calls?

    I have tried this piece of code:

     private delegate void UpdateUIDelegate(TextBox tbx, string text);
           
            private void SetText(TextBox tbx, string text)
            {
                UpdateUIDelegate action = new UpdateUIDelegate(UpdateUI);
                tbx.Dispatcher.BeginInvoke(action,tbx,text);           
            }

            private void UpdateUI(TextBox tbx, string text)
            {
                tbx.Text = text;
            }
           
            void btn_Click(object sender, RoutedEventArgs e)
            {
                //field.SolveRecursively(0);
                foreach (UIElement elem in LayoutRoot.Children)
                {
                    TextBox tbx= elem as TextBox;
                    if (tbx != null)
                    {
                        Thread thread = new Thread(new ThreadStart(SetText));
                        thread.Start();
                        Thread.Sleep(10);
                    }
                }
            }

    But I get "No overload for 'SetText' matches delegate 'System.Threading.ThreadStart' "

  • emil

    RE: Tip: Asynchronous Silverlight - Execute on the UI thread


    posted by emil on Jun 29, 2008 15:26

    Hi Dennis,

    For parameterized call you can use ParameterizedThreadStart(void (object) target) instead of ThreadStart(void () target).

    So instead of

    Thread thread = new Thread( new ThreadStart( ... ) );

    use

    Thread thread = new Thread( new ParameterizedThreadStart( SetText ) );

    where SetText takes one parameter:

    private void SetText( object o )
    {
     
    }

    Another way, my preferred, to achieve your goal is the following:

    ThreadStart ts = delegate()
    {
        DispatcherOperation op = 
            Dispatcher.BeginInvoke( 
                new Action<TextBox, string>( UpdateUI ), 
                tbx, 
                "custom text" );
    };

    So your final code would look like:

    private void UpdateUI( TextBox tbx, string text )
    {
        tbx.Text = text;
    }
     
    void btn_Click( object sender, RoutedEventArgs e )
    {
        //field.SolveRecursively(0);
        foreach ( UIElement elem in lr1.Children )
        {
            TextBox tbx = elem as TextBox;
            if ( tbx != null )
            {
                ThreadStart ts = delegate()
                {
                    DispatcherOperation op = 
                        Dispatcher.BeginInvoke( 
                            new Action<TextBox, string>( UpdateUI ), 
                            tbx, 
                            "custom text" );
                };
     
                Thread thread = new Thread( ts );
                thread.Start();
                Thread.Sleep( 10 );
            }
        }
    }

    Hope that helps!

  • -_-

    RE: Tip: Asynchronous Silverlight - Execute on the UI thread


    posted by pwr on Sep 13, 2009 05:46
    This article's been translated into Russian for silverlight.su
  • -_-

    RE: Tip: Asynchronous Silverlight - Execute on the UI thread


    posted by nz on Nov 05, 2009 00:31
    Hi Dennis, I try your solution, for some reason it is not asynchronous to the UI
  • -_-

    RE: Tip: Asynchronous Silverlight - Execute on the UI thread


    posted by Tom on Apr 23, 2010 14:48
    Great post, Dispatcher.BeginInvoke worked perfectly for me!
  • -_-

    RE: Tip: Asynchronous Silverlight - Execute on the UI thread


    posted by Doan Huynh on Jul 30, 2010 13:41
    Thank you, it is useful
  • -_-

    RE: Tip: Asynchronous Silverlight - Execute on the UI thread


    posted by Fruitcake on Aug 22, 2010 19:43

    Awesome !!

     

    Thanks for this sweet small example. Helped me.

  • -_-

    RE: Tip: Asynchronous Silverlight - Execute on the UI thread


    posted by murki on Nov 06, 2010 05:26

    what about updating objects that are bound to the UI (implementing INotifyPropertyChanged)??

  • -_-

    RE: Tip: Asynchronous Silverlight - Execute on the UI thread


    posted by chaits on Jan 10, 2011 14:18

    Thank you a lot!!!!

    It really helped me

  • shama

    Re: Tip: Asynchronous Silverlight - Execute on the UI thread


    posted by shama on Jun 17, 2013 09:24

    Hi,

    I have used the dispatcher timer to refresh my UI once i have database changes but only when the page get loaded the new data binds i want the data to be refreshed with the time plz help me with this...

Add Comment

Login to comment:
  *      *