Silverlight Background Workers

.Silverlight Background Workers

"Silverlight does not offer all the threading capabilities of the full .Net framework, but it does provide a BackgroundWorker class for running time-consuming code on a background thread."

Theading is frequently used to push time-consuming tasks to a separate thread. By default, Silverlight code runs in the UI thread. Time-consuming tasks can block the UI thread and prevent user interaction until the task has completed. Silverlight does not offer all the threading capabilities of the full .Net framework, but it does provide a BackgroundWorker class for running time-consuming code on a background thread.

  1. The BackgroundWorker class allows you to run an operation on a separate, dedicated thread. Time-consuming operations, such as downloads and database transactions, can cause your user interface to stop responding. When you want a responsive user interface and you must perform time-consuming operations, the BackgroundWorker class provides a convenient solution.

  2. To run an operation in the background, create a BackgroundWorker. You can listen for events that report the progress of your operation and signal when your operation is completed.

  3. To set up a background operation, add an event handler for the DoWork event. Call your time-consuming operation in this event handler. To start the background operation, call the RunWorkerAsync method. To receive notifications of progress updates, handle the ProgressChanged event. To receive a notification when the operation is completed, handle the RunWorkerCompleted event.

  4. You must be careful not to manipulate any user-interface objects in your DoWork event handler. Instead, communicate to the user interface through the ProgressChanged and RunWorkerCompleted events.

  5. Backgroundworker Events:
    • DoWork - This event is raised when you call the RunWorkerAsync method. This is where you start the time-consuming operation. Your code in the DoWork event handler should periodically check the CancellationPending property value and stop the operation if it is true. When this occurs, you set the Cancel property of System.ComponentModel.DoWorkEventArgs to true, and the Cancelled property of System.ComponentModel.RunWorkerCompletedEventArgs in your RunWorkerCompleted event handler will be set to true.

    • ProgressChanged - This event is raised when you call the ReportProgress method.

    • RunWorkerCompleted - This event is raised when the DoWork event handler returns. If the operation completes successfully and its result is assigned in the DoWork event handler, you can access the result through the RunWorkerCompletedEventArgs.Result property. The Error property of System.ComponentModel.RunWorkerCompletedEventArgs indicates that an exception was thrown by the operation. The Cancelled property of System.ComponentModel.RunWorkerCompletedEventArgs indicates whether a cancellation request was processed by the background operation. If your code in the DoWork event handler detects a cancellation request by checking the CancellationPending property and setting the Cancel property of System.ComponentModel.DoWorkEventArgs to true, the Cancelled property of System.ComponentModel.RunWorkerCompletedEventArgs will also be set to true.
  6. Backgroundworker Properties:
    • WorkerReportsProgress - gets or sets a boolean value indicating whether the background operation can report its progress.

    • WorkerSupportsCancellation - gets or sets a boolean value indicating if the background operation can be cancelled.
  7. Below is an app that starts a background worker when the button is pressed. The progress bar tracks the work completion.

Get Microsoft Silverlight


Background Worker (Click Button to Start Worker)

MainPage.xaml

<UserControl x:Class="BackgroundWorker_Example.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    d:DesignHeight="200" d:DesignWidth="250">

    <Grid x:Name="LayoutRoot" Background="White"
          Height="200"
          Width="250">
        <Grid.RowDefinitions>
            <RowDefinition Height="63" />
            <RowDefinition Height="56*" />
            <RowDefinition Height="181*" />
        </Grid.RowDefinitions>
        <TextBlock Text="Push button to start background process."
                   Style="{StaticResource HeaderStyle}"
                   Margin="0,40,0,0">
        </TextBlock>
        <Button Content="Button"
                Grid.Row="1"
                Height="23"
                HorizontalAlignment="Center"
                Name="button1"
                VerticalAlignment="Center"
                Width="75"
                Click="button1_Click" />
        <StackPanel Width="100"
                    HorizontalAlignment="Center"
                    Grid.Row="2"
                    Margin="83,19,67,68">
            <TextBlock Text=""
                       TextAlignment="Center"
                       x:Name="txtBar" />
        </StackPanel>
        <ProgressBar Height="11"
                     x:Name="prgBar"
                     Margin="75,43,67,51"
                     Width="108"
                     Grid.Row="2" />
    </Grid>
</UserControl>


MainPage.xaml.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.ComponentModel;

namespace BackgroundWorker_Example
{
    public partial class MainPage : UserControl
    {
        private BackgroundWorker worker1;
        private Boolean workCompleted = false;

        public MainPage()
        {
            InitializeComponent();
            this.Loaded += new RoutedEventHandler(MainPage_Loaded);
        }

        void MainPage_Loaded(object sender, RoutedEventArgs e)
        {
            worker1 = new BackgroundWorker();
            worker1.WorkerReportsProgress = true;
            worker1.DoWork += new DoWorkEventHandler(worker1_DoWork);
            worker1.ProgressChanged += new ProgressChangedEventHandler(worker1_ProgressChanged);
            worker1.RunWorkerCompleted += new RunWorkerCompletedEventHandler(worker1_RunWorkerCompleted);           
        }

        void worker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            txtBar.Text = "Work completed!";
            workCompleted = true;
            prgBar.IsIndeterminate = true;
        }

        void worker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            prgBar.Value = e.ProgressPercentage;
        }

        void worker1_DoWork(object sender, DoWorkEventArgs e)
        {
            for (int i = 1; i < 10; i++)
            {
                System.Threading.Thread.Sleep(1000);
                worker1.ReportProgress(i * 11);
            }
        }

        private void button1_Click(object sender, RoutedEventArgs e)
        {
            if ((worker1.IsBusy != true) && (! workCompleted))
            {
                txtBar.Text = "Working ...";
                worker1.RunWorkerAsync();
            }

        }


    }
}


Dispatcher Class

The Dispatcher class allows you to communicate with the UI thread from background threads. A thread in Silverlight can not access data on another thread directly ("Invalid cross-thread access" exception). The Dispatcher object has a BeginInvoke method which allows access to the UI thread. An example when this method would be used is when you want to update a UI control from a background thread.


DispatcherTimer Class

The DispatcherTimer class is a timer that is integrated into the Dispatcher queue which is processed at a specified interval of time and at a specified priority. Reasons for using a DispatcherTimer opposed to a System.Timers.Timer are that the DispatcherTimer runs on the same thread as the Dispatcher and a DispatcherPriority can be set on the DispatcherTimer. A DispatcherTimer will keep an object alive whenever the object's methods are bound to the timer. Below, a DispatcherTimer object named dispatcherTimer is created. The event handler dispatcherTimer_Tick is added to the Tick event of dispatcherTimer. The Interval is set to 1 second using a TimeSpan object, and the timer is started.

.DispatchTimer Example Code



Reference Articles


Top