MULTIMEDIA

Silverlight Recipes : Updating the UI from a Background Thread

7/13/2011 6:07:19 PM

1. Problem

You need to update the UI from a background thread so that the UI can be responsive.

2. Solution

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 class supports progress reporting and cancellation. The Dispatcher class is useful when you need a simple way to queue up background work without progress reporting or cancellation.

3. How It Works

The .NET Framework for Silverlight includes the System.Threading namespace, which contains classes needed to manage a thread pool, launch threads, and synchronize threads, just like the full version of the .NET Framework.

As with most UI programming models such as Visual Basic 6, .NET Windows Forms, or WPF, it is not safe to access UI objects from a background thread. UI objects, such as Button, TextBox, and TextBlock objects, can only be safely accessed on the UI thread.

The role of the Dispatcher is to provide a way for a background thread to invoke a method that runs on the main thread so that it can safely update the UI. This approach is useful when you're retrieving data from the server using the asynchronous WebRequest class, as demonstrated in this recipe. Figure 1 shows the UI for the application after the data is downloaded.

Figure 1. Recipe 7 test UI

4. The Code

The sample application for this recipe contains a button titled Retrieve XML and Load that when clicked fires the event RetrieveXMLandLoad_Click. This event creates an HttpWebRequest object that points to the location where Recipe 2-5's ApressBooks.xml file was copied:

Uri location =
new Uri("http://localhost:9090/xml/ApressBooks.xml",UriKind.Absolute);
WebRequest request = HttpWebRequest.Create(location);
request.BeginGetResponse(
new AsyncCallback(this.RetrieveXmlCompleted), request);

When the asynchronous web request completes, the code in the callback method RetrieveXmlCompleted executes. The following code retrieves the XML document from the response stream and stores it in an XDocument object:

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);
...

The rest of the code in the callback method RetrieveXmlCompleted executes the same LINQ to XML as in Recipe 5 to obtain a List of ApressBook objects. The last line of code calls the Dispatcher object to queue UI work by calling BeginInvoke to execute the delegate and passing in the method DataBindListBox on the UI thread passing in the List of ApressBook objects:

Dispatcher.BeginInvoke(() => DataBindListBox(_apressBookList));

The syntax looks a bit strange if you are not familiar with C# lambda expressions. The syntax is shorthand for creating a delegate object and mashing the parameters into the call. The method DataBindListBox has a single line of code to assign the ItemsSource property on the BooksListBox object:

BooksListBox.ItemsSource = list;

If you skip using the Dispatcher in the callback method RetrieveXmlCompleted for the HttpWebRequest and instead put the line of code to assign the ItemsSource property in the callback method directly, the UI will not be updated because the callback method returns on the background thread of the HttpWebRequest, not the UI thread. By calling Dispatcher.BeginInvoke to update the UI from the HttpWebRequest callback background thread, you queue the work to assign the List object to the ItemsSource so that it safely executes when the main UI thread literally has cycles available. Listings 1 and 2 show the source code for this recipe's test application.

Listing 1. Recipe 8's MainPage.xaml File
<UserControl x:Class="Ch02_ProgrammingModel.Recipe2_8.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="300" d:DesignWidth="400"> <Grid x:Name="LayoutRoot" Background="White"
Margin="6,6,6,6">
<StackPanel>
<Button Content="Retrieve XML and Load"
Click="RetrieveXMLandLoad_Click"></Button>
<ListBox x:Name="BooksListBox" Margin="4,4,4,4" Height="452" >
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Margin="2,2,2,2">
<TextBlock Text="{Binding Path=ISBN}" Margin="0,0,0,2"/>
<TextBlock Text="{Binding Path=Title}" Margin="0,0,0,2"/>
<TextBlock Width="550" Text="{Binding Path=Description}"
TextWrapping="Wrap" Margin="0,0,0,10"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
</Grid>
</UserControl>


Listing 2. Recipe 8's MainPage.xaml.cs File
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Xml.Linq;

namespace Ch02_ProgrammingModel.Recipe2_8
{
public partial class MainPage : UserControl
{
public MainPage()
{
InitializeComponent();
}

private void RetrieveXMLandLoad_Click(object sender, RoutedEventArgs 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);
}

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)
//Dispatcher.BeginInvoke(
// delegate()
// {
// DataBindListBox(_apressBookList);
// }
// );
//Use C# 3.0 Lambda
Dispatcher.BeginInvoke(() => DataBindListBox(_apressBookList));
}

void DataBindListBox(List<ApressBook> list)
{
BooksListBox.ItemsSource = list;
}
}


public class ApressBook
{
public string Author { get; set; }
public string Title { get; set; }
public string ISBN { get; set; }
public string Description { get; set; }
public DateTime PublishedDate { get; set; }
public string NumberOfPages { get; set; }
public string Price { get; set; }
public string ID { get; set; }
}
}
Other  
  •  Programming with DirectX : Sound in DirectX - XAudio2
  •  Programming with DirectX : Sound in DirectX - XACT3 (part 2) - XACT3 Demo
  •  Programming with DirectX : Sound in DirectX - XACT3 (part 1) - XACT3 Tools
  •  iPhone 3D Programming : Image-Processing Example: Bloom
  •  iPhone 3D Programming : Anisotropic Filtering: Textures on Steroids
  •  iPhone 3D Programming : Reflections with Cube Maps
  •  Silverlight Recipes : Networking and Web Service Integration - Accessing Resources over HTTP
  •  Silverlight Recipes : Networking and Web Service Integration - Using JSON Serialization over HTTP
  •  Microsoft XNA Game Studio 3.0 : Displaying Images - Using Resources in a Game (part 4) - Filling the Screen
  •  Microsoft XNA Game Studio 3.0 : Displaying Images - Using Resources in a Game (part 3) - Sprite Drawing with SpriteBatch
  •  Microsoft XNA Game Studio 3.0 : Displaying Images - Using Resources in a Game (part 2) - Positioning Your Game Sprite on the Screen
  •  Microsoft XNA Game Studio 3.0 : Displaying Images - Using Resources in a Game (part 1) - Loading XNA Textures
  •  iPhone 3D Programming : Holodeck Sample (part 5) - Overlaying with a Live Camera Image
  •  iPhone 3D Programming : Holodeck Sample (part 4) - Replacing Buttons with Orientation Sensors
  •  iPhone 3D Programming : Holodeck Sample (part 3) - Handling the Heads-Up Display
  •  iPhone 3D Programming : Holodeck Sample (part 2) - Rendering the Dome, Clouds, and Text
  •  iPhone 3D Programming : Holodeck Sample (part 1) - Application Skeleton
  •  Building LOB Applications : Printing in a Silverlight LOB Application
  •  Building LOB Applications : Data Validation through Data Annotation
  •  Building LOB Applications : Implementing CRUD Operations in RIA Services
  •  
    Top 10
    Debugging Tools For Parallel Tasks
    Architecting a SharePoint 2010 Deployment : Understanding the Reasons for Deploying Multiple Farms
    Database Availability Group Replication in Exchange Server 2010 : Comparing and Contrasting DAG Versus CCR/SCR/SCC
    Programming with DirectX : Additional Texture Mapping - Alpha Mapping
    Windows Server 2008 R2 Active Directory Domain Services Primer : Understanding Domain Trusts
    Building Your First Windows Phone 7 Application (part 5) - Styling Your Application
    Windows Phone 7 Development : Adding a WebBrowser Control
    Exploring Group Policy in Windows 7
    A Second Silverlight Example: Creating a Project
    Building LOB Applications : Implementing CRUD Operations in WCF Data Services
    Most View
    SQL Server 2008 : Transact-SQL Programming - TRY...CATCH Logic for Error Handling
    The SQL Programming Language : Complex Queries and Join Queries (part 3)
    Windows Server 2008 : Understanding Active Directory Sites (part 1)
    IIS 7.0 : Securing Configuration - Securing Sensitive Configuration
    Deploying a Windows Server 2008 R2 Network Policy Server
    Windows Azure : Processing with worker roles - Communicating with a worker role
    Introducing Windows Mail
    Windows 7 : General Maintenance Tools (part 3) - Checking Your Disks for Errors & Optimizing Disk Performance
    Configuring Windows 7 NIC Devices (part 1) - Configuring a Network Adapter & Troubleshooting a Network Adapter
    Windows 7 : Windows Driver Foundation Architecture (part 4) - Tools for Development and Testing
    SharePoint 2010 : Operations Management with the SharePoint Central Administration Tool (part 4) - Reviewing Backup and Restore Settings in SPCA
    WCF Services : Data Contract - Equivalence
    Mobile Application Security : SMS Security - Protocol Attacks (part 1)
    Consuming a WCF Service with Silverlight
    Silverlight Recipes : Managing XAML Resources
    Programming .NET Security : Asymmetric Encryption Explained (part 1) - Creating Asymmetric Keys
    Default Security Policy
    Using Non-Windows Systems to Access Exchange Server 2010 : Outlook Express
    Setting Default Internet Programs
    ASP.NET State Management Techniques : The Role of the Global.asax File