Logo
HOW TO
Windows XP
Windows Vista
Windows 7
Windows Azure
Windows Server
Windows Phone
 
 
Windows Phone

Windows Phone 7 Programming Model : Asynchronous Programming - Background Threads

4/14/2013 6:37:10 PM

This section covers asynchronous programming, which is the preferred method do to work in Silverlight. Asynchronous programming is preferred, because it takes work off of the UI thread, which should be a priority in order to maximize UI performance for animation and transitions.

Rendering performance can be improved two ways: pushing work from the UI thread to the Render thread and pushing work such as processing remote data to a separate background thread. We cover various ways to push work off of the UI thread in this section.

1. Background Threads

You can use the standard .NET multithreading class Thread.Start and ThreadPool.QueueUserWorkItem to perform background work that writes data to isolated storage, and it will work fine. If you try to access the XAML from the standard classes without taking extra steps, it will throw an exception. Silverlight includes classes that make it easier to perform background work that interacts with the user interface.

1.1. Fire and Forget Background Processing

The Dispatcher class offers a safe way to call a method that updates the UI asynchronously from a background thread by providing services for managing the queue of work items for a thread. Both the Dispatcher and the BackgroundWorker classes can perform work on a separate thread. The BackgroundWorker classsupports progress reporting and cancellation, which we cover in detail in the next section. The Dispatcher class is useful when you need a simple way to queue up background work without progress reporting or cancellation.

You can create a delegate and then user Dispatcher.BeginInvoke to fire the delegate, which then updates the UI. As an example, if you have a TextBlock named TextBlock1 that you need to update from a background thread, obtain the Dispatcher from that control and perform the update. Here is an example of using C# lambda syntax (=>).

TextBlock1.Dispatcher.BeginInvoke(() =>
  {
     TextBlock1.Text = "Data Updated";
  };

You can call Dispatcher.CheckAccess to determine if the calling thread is on the same thread as the control or the UI thread. Use BeginInvoke it if returns false. It is recommended to obtain the Dispatcher instance from the control closest to the controls being updated. So if multiple controls need to be updated and they are contained in a Grid panel, obtain the Dispatcher from the Grid.

A file named ApressBooks.xml is added to the AsynchronousProgramming project. This XML file contains a simple xml schema with a few book titles in it. Here is one record from the XML file:

<ApressBook>
  <ID>4</ID>
  <ISBN>1-4302-2435-5</ISBN>
  <Author>Jit Ghosh and Rob Cameron</Author>
  <Title>Silverlight Recipes: A Problem-Solution Approach, Second Edition</Title>
  <Description>Silverlight Recipes: A Problem-Solution Approach, Second Edition is your
practical

companion to developing rich, interactive web applications with Microsoft's latest
technology. </Description> <DatePublished>2010-07-15T00:00:00</DatePublished> <NumPages>1056</NumPages> <Price>$49.99</Price> </ApressBook>

The UI for the DispatcherPage.xaml contains a ListBox with an ItemTemplate to display the above data and an application bar with one button to load the data. When the button is clicked, the LoadDataAppBarButton_Click event handler spins up a WebRequest object that points to the local developer web server from the WcfRemoteServices Project to retrieve the XML file. Here is the code snippet for the application bar button event handler:

private void LoadDataAppBarButton_Click(object sender, EventArgs e)
{
  Uri location =
      new Uri("http://localhost:9090/xml/ApressBooks.xml", UriKind.Absolute);
  WebRequest request = HttpWebRequest.Create(location);
  request.BeginGetResponse(
      new AsyncCallback(this.RetrieveXmlCompleted), request);
}

					  

All remote service calls MUST be executed asynchronously, so the callback function named RetrieveXmlCompleted is where the results are actually returned to the application. Here is the RetrieveXmlCompleted method:

void RetrieveXmlCompleted(IAsyncResult ar)
{
  List<ApressBook> _apressBookList;
  HttpWebRequest request = ar.AsyncState as HttpWebRequest;
  WebResponse response = request.EndGetResponse(ar);
  Stream responseStream = response.GetResponseStream();
  using (StreamReader streamreader = new StreamReader(responseStream))
  {
    XDocument xDoc = XDocument.Load(streamreader);
    _apressBookList =
    (from b in xDoc.Descendants("ApressBook")
      select new ApressBook()
      {
        Author = b.Element("Author").Value,
        Title = b.Element("Title").Value,
        ISBN = b.Element("ISBN").Value,
        Description = b.Element("Description").Value,
        PublishedDate = Convert.ToDateTime(b.Element("DatePublished").Value),
        NumberOfPages = b.Element("NumPages").Value,
        Price = b.Element("Price").Value,
        ID = b.Element("ID").Value
      }).ToList();
  }
  //Could use Anonymous delegate (does same as below line of code)
  // BooksListBox.Dispatcher.BeginInvoke(
  //  delegate()
  //  {
  //    DataBindListBox(_apressBookList);

					  

//  }
  // );
  //Use C# 3.0 Lambda
  BooksListBox.Dispatcher.BeginInvoke(() => DataBindListBox(_apressBookList));
}

					  

The xml file is received and then loaded into an XDocument object for some basic Linq to XML manipulation to turn it into a collection of APressBook .NET objects. Once that little bit of work is completed, the collection needs to be pushed back to the UI thread. This is where the BooksListBox.Dispatcher is finally used to fire the DataBindListBox method to perform the data binding.

The previous code snippet includes an alternative method of passing the _apressBookList to the UI thread and databind. It could be reduced further to the following:

BooksListBox.Dispatcher.BeginInvoke(() =>
{
  BooksListBox.ItemsSource = _apressBookList;
});

To test the Dispatcher using WebRequest, both the WCFRemoteServices project and the AsynchronousProgramming project must be running.Right-click on the Ch04_WP7ProgrammingModel Solution and configure it to have multiple startup projects, as shown in Figure 1.

Figure 1. WebBrowser control scripting page

If you still want to use standard .NET Framework threading, You can call SynchronizationContext.Current to get the current DispatcherSynchronizationContext, assign it to a member variable on the Page, and call Post(method, data) to fire the event back on the UI thread. Calling Send(method, data) instead of Post will make a synchronous call, which you should avoid doing if possible as it could affect UI performance.

1.2. Supporting Progress Reporting and Cancellation

For long running processes, having the ability to cancel work as well as show work progress is necessary for a good user experience. A convenient class that provides a level of abstraction as well as progress updates is the System.ComponentModel.BackgroundWorker class. The BackgroundWorker class lets you indicate operation progress, completion, and cancellation in the Silverlight UI. For example, you can check whether the background operation is completed or canceled and display a message to the user.

To use a background worker thread, declare an instance of the BackgroundWorker class at the class level, not within an event handler:

BackgroundWorker bw = new BackgroundWorker();

You can specify whether you want to allow cancellation and progress reporting by setting one or both of the WorkerSupportsCancellation and WorkerReportsProgress properties on the BackgroundWorker object to true. The next step is to create an event handler for the BackgroundWorker.DoWork event. This is where you put the code for the time-consuming operation. Within the DoWork event, check the CancellationPending property to see if the user clicked the Cancel button. You must set e.Cancel = true in DoWork so that WorkCompleted can check the value and finish correctly if the work was completed.

If the operation is not cancelled, call the ReportProgress method to pass a percentage complete value that is between 0 and 100. Doing this raises the ProgressChanged event on the BackgroundWorker object. The UI thread code can subscribe to the event and update the UI based on the progress. If you call the ReportProgress method when WorkerReportsProgress is set to false, an exception will occur. You can also pass in a value for the UserState parameter, which in this case is a string that is used to update the UI.

Once the work is completed successfully, pass the data back to the calling process by setting the e.Result property of the DoWorkerEventArgs object to the object or collection containing the data resulting from the work. The DoWorkerEventArgs.Result is of type object and can therefore be assigned any object or collection of objects. The value of the Result property can be read when the RunWorkerCompleted event is raised upon completion of the operation and the value can be safely assigned to UI object properties. Listing 1 shows the XAML modifications in the ContentPanelGrid.

Example 1. The BackgroundWorkerPage.xamlContentPanel XAML
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
  <StackPanel Orientation="Vertical" d:LayoutOverrides="Height">
    <StackPanel x:Name="StatusStackPanel" Orientation="Vertical">
      <StackPanel Orientation="Horizontal" d:LayoutOverrides="Width">
        <TextBlock x:Name="processingStateTextBlock" TextWrapping="Wrap"
            VerticalAlignment="Top" Width="190" Margin="12,34,0,0"/>
        <Button x:Name="cancelButton" Content="Cancel Operation"
            VerticalAlignment="Top" Click="cancelButton_Click" Width="254" />
      </StackPanel>
      <ProgressBar x:Name="BookListDownloadProgress" Width="456"
                        HorizontalAlignment="Left" />
      </StackPanel>
      <ListBox x:Name="BooksListBox" ItemsSource="{Binding ApressBookList}"
        Height="523" ItemTemplate="{StaticResource BookListBoxDataTemplate}" />
  </StackPanel>
</Grid>

					  

The StatusStackPanel container that has the status info is made visible when the work is started, and is then hidden since the work is completed. Figure 2 has the UI.

Figure 2. WebBrowser control scripting page

One additional wrinkle is that the code overrides OnNavigateFrom. If the BackgroundWorker thread is busy, the code cancels the operation, since the user navigated away. Listing 2 has the full source code.

Example 2. The BackgroundWorkerPage.xaml.cs Code File
using System.ComponentModel;
using System.Windows;
using Microsoft.Phone.Controls;

namespace AsynchronousProgramming.pages
{
  public partial class BackgroundWorkerPage : PhoneApplicationPage
  {
    private BackgroundWorker _worker = new BackgroundWorker();

    public BackgroundWorkerPage()
    {
      InitializeComponent();

//Configure BackgroundWorker thread
      _worker.WorkerReportsProgress = true;
      _worker.WorkerSupportsCancellation = true;
      _worker.DoWork +=
        new DoWorkEventHandler(worker_DoWork);
      _worker.ProgressChanged +=
        new ProgressChangedEventHandler(worker_ProgressChanged);
      _worker.RunWorkerCompleted +=
        new RunWorkerCompletedEventHandler(worker_RunWorkerCompleted);

      //Kick off long running process
      //Make status visible
      _worker.RunWorkerAsync();
      StatusStackPanel.Visibility = Visibility.Visible;
    }

    protected override void OnNavigatedFrom(
                     System.Windows.Navigation.NavigationEventArgs e)
    {
      //Cancel work if user navigates away
      if (_worker.IsBusy)
        _worker.CancelAsync();

      base.OnNavigatedFrom(e);
    }

    void worker_DoWork(object sender, DoWorkEventArgs e)
    {
      ApressBooks books = new ApressBooks();
      books.LoadBooks();
      int progress;
      string state = "initializing...";
      //Do fake work to retrieve and process books
      for (int i = 1; i <= books.ApressBookList.Count;i++ )
      {
        if (_worker.CancellationPending == true)
        {
          e.Cancel = true;
          break;
        }
        else
        {
          progress = (int)System.Math.Round((double)i /
                     books.ApressBookList.Count * 100d);

          if ((progress > 15) && (progress < 90))
            state = "processing..." ;
          if (progress > 85)
            state = "finishing..." ;
          if (progress == 95)
            state = "Loading complete.";

          _worker.ReportProgress(progress, state);
          System.Threading.Thread.Sleep(250);

					  

}
      }
      e.Result = books;
    }

    void worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
    {
      BookListDownloadProgress.Value = e.ProgressPercentage;
      processingStateTextBlock.Text = e.UserState as string;
    }


    void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
      if (e.Cancelled == true)
        MessageBox.Show("Operation cancelled.","Cancelled",MessageBoxButton.OK);
      else
        LayoutRoot.DataContext = e.Result as ApressBooks;

      //Clean up status UI
      BookListDownloadProgress.Value = 0;
      processingStateTextBlock.Text = "";
      StatusStackPanel.Visibility = Visibility.Collapsed;
    }

    private void cancelButton_Click(object sender, RoutedEventArgs e)
    {
      _worker.CancelAsync();
    }
  }
}

					  

Silverlight includes the standard .NET locking primitives, such as Monitor or lock, as well as the ManualResetEvent class where deadlocks can occur. A deadlock occurs when two threads each hold on to a resource while requesting the resource that the other thread is holding. A deadlock will cause the application to hang. It is easy to create a deadlock with two threads accessing the same resources in an application.

The BackgroundWorker class tries to prevent deadlocks or cross-thread invocations that could be unsafe.

Any exceptions that can occur must be caught within the background thread, because they will not be caught by the unhandled exception handler at the application level. If an exception occurs on the background thread, one option is to catch the exception and set Result to null as a signal that there was an error. Another option is to set a particular value to Result as a signal that a failure occurred.

Other -----------------
- Windows Phone 8 : Designing for the Phone - Deciding on an Application Paradigm
- Windows Phone 8 : Designing for the Phone - The Third Screen
- Windows Phone 7 Programming Model : Web Browser Control
- Windows Phone 7 Programming Model : Tasks
- Windows Phone 7 Programming Model : Application Data Persistence
- Windows Phone 7 Programming Model : Device Information
- iphone Programming : Mixing OpenGL ES and UIKit, Rendering Confetti, Fireworks, and More: Point Sprites
- iphone Programming : Animation with Sprite Sheets, Image Composition and a Taste of Multitexturing
- XNA Game Studio 3.0 : Creating Game Components - Adding Game Sounds
- Iphone Application : Using Gesture Recognizers (part 4)
 
 
REVIEW
- First look: Apple Watch

- 10 Amazing Tools You Should Be Using with Dropbox

- 3 Tips for Maintaining Your Cell Phone Battery (part 1)

- 3 Tips for Maintaining Your Cell Phone Battery (part 2)
 
VIDEO TUTORIAL
- How to create your first Swimlane Diagram or Cross-Functional Flowchart Diagram by using Microsoft Visio 2010 (Part 1)

- How to create your first Swimlane Diagram or Cross-Functional Flowchart Diagram by using Microsoft Visio 2010 (Part 2)

- How to create your first Swimlane Diagram or Cross-Functional Flowchart Diagram by using Microsoft Visio 2010 (Part 3)
 
Popular tags
Microsoft Access Microsoft Excel Microsoft OneNote Microsoft PowerPoint Microsoft Project Microsoft Visio Microsoft Word Active Directory Biztalk Exchange Server Microsoft LynC Server Microsoft Dynamic Sharepoint Sql Server Windows Server 2008 Windows Server 2012 Windows 7 Windows 8 Adobe Indesign Adobe Flash Professional Dreamweaver Adobe Illustrator Adobe After Effects Adobe Photoshop Adobe Fireworks Adobe Flash Catalyst Corel Painter X CorelDRAW X5 CorelDraw 10 QuarkXPress 8 windows Phone 7 windows Phone 8 BlackBerry Android Ipad Iphone iOS
Popular keywords
HOW TO Swimlane in Visio Visio sort key Pen and Touch Creating groups in Windows Server Raid in Windows Server Exchange 2010 maintenance Exchange server mail enabled groups Debugging Tools Collaborating
Top 10
- Microsoft Excel : How to Use the VLookUp Function
- Fix and Tweak Graphics and Video (part 3) : How to Fix : My Screen Is Sluggish - Adjust Hardware Acceleration
- Fix and Tweak Graphics and Video (part 2) : How to Fix : Text on My Screen Is Too Small
- Fix and Tweak Graphics and Video (part 1) : How to Fix : Adjust the Resolution
- Windows Phone 8 Apps : Camera (part 4) - Adjusting Video Settings, Using the Video Light
- Windows Phone 8 Apps : Camera (part 3) - Using the Front Camera, Activating Video Mode
- Windows Phone 8 Apps : Camera (part 2) - Controlling the Camera’s Flash, Changing the Camera’s Behavior with Lenses
- Windows Phone 8 Apps : Camera (part 1) - Adjusting Photo Settings
- MDT's Client Wizard : Package Properties
- MDT's Client Wizard : Driver Properties
 
Windows XP
Windows Vista
Windows 7
Windows Azure
Windows Server
Windows Phone
2015 Camaro