Working with background threads

Threading is a well-known feature to push time consuming tasks to a separate thread. Silverlight, by default, is running our code in the UI thread, which means that if we have a method that takes time to finish, the UI thread will be blocked and won't allow interaction until the method finishes running. By pushing this time consuming method to a different thread, the method will still take time to complete, but the UI thread won't be blocked, and the user can keep interacting with it while the method computes.

While Silverlight doesn't offer all of the threading options that the full .NET framework offers, it's still powerful enough to drive multithreaded applications. One important aspect you have to remember when working with threads is that your code runs in a whole other environment than your UI, and as such you cannot access any of the elements in the UI thread. Don't fear though; a workaround for this will be shown shortly.

Spawning a background thread to execute code

The BackgroundWorker class of Silverlight provides us an easy way to run time-consuming code on a background thread. Moving time consuming calculations to this background thread will free our UI thread from freezing, and give our users the sense of interactivity that they are expecting. The BackgroundWorker class provides two important properties for interacting with it as follows:

  • WorkerSupportsCancellation: A Boolean property indicating whether the background operation can be cancelled or not
  • WorkerReportsProgress: A Boolean property indicating whether the background operation should report its progress or not

But these properties aren't the reason for which we have gathered here today. The one most important aspect of the BackgroundWorker class is, without a doubt, the DoWork event handler.

This event handler is where you run your resource hog code on the background thread. If we wish to report progress from the DoWork event handler to the calling process, we call the ReportProgress method, passing it a completion percentage from 0 to 100. Take note though, if we set the WorkerReportProgress property of the BackgroundWorker class to false, the ReportProgress method will throw an exception. To pass data back to the calling process (which is basically the whole point of this event handler), we set the Result property of the DoWorkEventArgs object, which is passed to the event handler. We can read this value when the RunWorkerCompleted event is raised, at the end of the operation. Let's get a first-hand impression on how to use this awesome feature!

Creating your first BackgroundWorker

To get started, fire up Visual Studio 2010, and load the Chapter4-BackgroundWorker project from the training files. The project right now is nothing more than a header and a vertical StackPanel consisting of a TextBlock element and a ProgressBar element. We will use the ProgressBar element to show the progress of our background worker in a nice graphical way. Perform the following steps:

  1. Switch over to the MainPage.xaml.cs file and add the following using command:
    using System.ComponentModel;
    
  2. Next, add the following private variable:
    private BackgroundWorker _bw;
    

    This will be our BackgroundWorker object. We will initiate it under the Loaded event handler.

  3. Inside the MainPage constructor, add the following line of code to create an event handler for the Loaded event:
    this.Loaded += new RoutedEventHandler(MainPage_Loaded);
    
  4. Now, add the following event handler method:
    void MainPage_Loaded(object sender, RoutedEventArgs e)
    {
    _bw = new BackgroundWorker();
    _bw.WorkerReportsProgress = true;
    _bw.DoWork += new DoWorkEventHandler(_bw_DoWork);
    _bw.ProgressChanged += new ProgressChangedEventHandler(_bw_ProgressChanged);
    _bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(_bw_RunWorkerCompleted);
    _bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(_bw_RunWorkerCompleted);
    _bw.RunWorkerAsync();
    }
    

    The preceding code snippet initiates the BackgroundWorker object and sets its WorkerReportProgress property to true. By setting this property to true, we enable the background worker to report the progress of its task.

  5. Next, we are adding three event handlers:
    • DoWork: This event handler gets called once we call the RunWorkerAsync method of BackgroundWorker.
    • ProgressChanged: This event handler gets called whenever we call the ReportProgress method of BackgroundWorker.
    • RunWorkerCompleted: This event handler gets called once the background operation is completed
  6. Once everything is set up, we will call the RunWorkerAsync method to get the background operation started.

    Let's add the event handler methods now, starting with the DoWork handler.

  7. Add the following code snippet to your MainPage.xaml.cs:
    void _bw_DoWork(object sender, DoWorkEventArgs e)
    {
    for (int i = 1; i < 11; i++)
    {
    System.Threading.Thread.Sleep(1000);
    _bw.ReportProgress(i * 10);
    }
    }
    

    What we have here is a simple for loop, going from 1 to 10, which will suspend the currently used thread for one second at a time. Once it wakes up, the method will call the ReportProgress method of the BackgroundWorker object passing it the percentage of job completion.

    Note

    Quick quiz: What do you think would have happened if you ran this exact method on the UI thread? Would everything still run as smoothly as expected?

    The two remaining event handlers—ProgressChanged and RunWorkerCompleted—update the UI with the progress of the background operation.

  8. Add the following code snippet to your MainPage.xaml.cs file:
    void _bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
    lblBar.Text = "Loading completed!";
    prgsBar.IsIndeterminate = true;
    }
    void _bw_ProgressChanged(object sender, ProgressChangedEventArgs e)
    {
    prgsBar.Value = e.ProgressPercentage;
    }
    

Nothing particularly interesting is happening in these two methods. The RunWorkerCompleted method updates the TextBlock and ProgressBar elements to indicate that the operation is completed. The ProgressChanged method makes use of the ProgressPercentage property of ProgressChangedEventArgs to set the ProgressBar element's value. Remember, we have passed the ProgressChanged event handler the value to update using the ReportProgress method earlier.

Everything is done! Build and run your application, and you should get the following screenshot:

Creating your first BackgroundWorker

The progress bar will keep filling up, reporting the progress made in the background operation until it gets to 100 percent.

And with this project running, you've finished creating your first BackgroundWorker based application.

Using the Dispatcher object

When working with different threads, there may come a time when you have to deal with cross threading. Imagine a case, where your background thread needs to update a control, which is positioned on the UI thread. If you try to update it directly, you'll be greeted with the "Invalid cross-thread access" exception. This happens because a thread in Silverlight cannot access data on another thread directly. So how do you update the UI thread from another thread? You use the Dispatcher object.

The Dispatcher object exposes the BeginInvoke method, which allows us to access the UI thread very easily. Consider the following line of code:

Dispatcher.BeginInvoke(UpdateUI);

The preceding line of code will call the UpdateUI method on the UI thread itself, and not the background thread it was called from.

If you want, you can use lambda expressions to make this code event shorter:

Dispatcher.BeginInvoke(() => { prgsBar.Value = 70; });

The Dispatcher object is a very important concept of Silverlight threading, because without it, we wouldn't be able to interact with the UI thread from any background thread at all. This is the key object that is in charge of the cross threading between the background and UI threads.

Working with the DispatcherTimer object

The DispatcherTimer object represents, well, a timer. The difference between the DispatcherTimer object and any other timer class you might have encountered before is that the DispatcherTimer object is integrated into the Dispatcher queue. This means that the timer is processed at a specified interval of time and at a specified priority. The DispatcherTimer object exposes the Interval property, which accepts a TimeSpan object and lets you decide the interval at which the timer will execute. Consider the following code snippet:

DispatcherTimer timer = new DispatcherTimer();
timer.Interval = new TimeSpan(0, 0, 20);
timer.Tick += new EventHandler(timer_Tick);
timer.Start();

The preceding code snippet initiates a DispatcherTimer object called timer, and then sets its interval to 20 seconds. The Tick handler defines the handler that gets called once the interval has elapsed. To get the timer started, you have to call the Start method of the object. If you wish to stop the timer at any point, you just call the Stop method. Create a new Silverlight 4 project in Visual Studio and copy the preceding code snippet to your MainPage.xaml.cs file. If you put a breakpoint at the beginning of the timer_Tick method, you would notice it breaks at that point every 20 seconds. Try to play around with the TimeSpan object, and the Start and Stop methods of the DispatcherTimer object to get a better understanding of this mechanism.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.21.248.162