Downloading Content on Demand in Silverlight

Microsoft Silverlight provides a Downloader object, which represents a set of download functionality for a Silverlight plug-in.

This topic contains the following sections:

  • Role of the Downloader Object
  • Creating and Initializing a Downloader Object
  • Defining Downloader Event Handlers
  • Retrieving Downloaded Content
  • Using SetFontSource for Downloaded Fonts
  • Handling Downloader Errors
  • URI Protocols and Schemes

Role of the Downloader Object

The Downloader object is a special-purpose Silverlight object that provides the ability to download application data, such as XAML content, JavaScript content, or media assets such as images. The Downloader object can download content on demand in response to application needs, instead of having to provide all application content when the Silverlight plug-in is instantiated. More importantly, you can render downloaded content without having to refresh the entire Web page. The Downloader object provides functionality for initiating the data transfer, monitoring the progress of the data transfer, and retrieving the downloaded content. You can also use the Downloader to preload content in the background such that the content that might be requested through user action in the UI (such as the user choosing to play a different media file) can initiate more quickly in some circumstances.

The properties and methods of the Downloader object are modeled after the XMLHttpRequest (XHR) set of APIs. XMLHttpRequest provides JavaScript and other Web browser scripting languages the ability to transfer XML data to and from a Web server by using HTTP, and to manipulate the data.

Download Packages

Silverlight provides the ability to download content as a package, which is a collection of independent files that contain XAML content, media assets, and other application data. The .zip file format is supported as a download package. The following illustration shows a collection of application content that is contained within a .zip download package.

Download package containing a collection of application files

Download package containing a collection of application files

When the package is successfully downloaded, you can use methods such as GetResponseText, SetSource (Image), SetSource (MediaElement), and CreateFromXamlDownloader to selectively retrieve a specific named part of the package. In this case, a part reference is the file name of the individual content within the package, such as "rotation01_green.png".

Retrieving a part from a download package

Retrieving a part from a download package

Creating and Initializing a Downloader Object

To create a Downloader object, use the CreateObject method of the Silverlight plug-in, and retain a reference to the returned object (this is the created Downloader). Do not invoke the CreateObject method until the Silverlight plug-in has been instantiated. The following JavaScript example shows how to invoke the CreateObject method to create a Downloader object.

JavaScript
// Event handler for initializing and executing a download request.
function onMouseLeftButtonUp(sender, eventArgs)
{
    // Retrieve a reference to the plug-in.
    var slPlugin = sender.getHost();
    // Create a Downloader object.
    var downloader = slPlugin.createObject("downloader");
}

Because the Downloader operates asynchonously, most of the interaction that your code has with the Downloader object will be through event handlers. Typically this involves defining event-handler functions for two Downloader events, DownloadProgressChanged and Completed. The DownloadProgressChanged event, enables you to monitor the progress of the download. One way of responding to this event is to provide visual feedback, such as a progress bar indicator.

The Completed event enables you to determine the state of the download request. The following JavaScript example shows how to add DownloadProgressChanged and Completed events to a Downloader object.

JavaScript
// Event handler for initializing and executing a download request.
function onMouseLeftButtonUp(sender, eventArgs)
{
    // Retrieve a reference to the plug-in.
    var slPlugin = sender.getHost();
    // Create a Downloader object.
    var downloader = slPlugin.createObject("downloader");
    // Add DownloadProgressChanged and Completed events.
    downloader.addEventListener("downloadProgressChanged", onDownloadProgressChanged);
    downloader.addEventListener("completed", onCompleted);
}

The next step involves initializing the download request by using the Open method, and executing the download request by using the Send method. The Open method initializes the Downloader object by specifying the action to perform and the content to perform the action on. The following JavaScript example shows the completed sequence of how to create, initialize, and execute a download request:

JavaScript
// Event handler for initializing and executing a download request.
function onMouseLeftButtonUp(sender, eventArgs)
{
    // Retrieve a reference to the plug-in.
    var slPlugin = sender.getHost();
    // Create a Downloader object.
    var downloader = slPlugin.createObject("downloader");
    // Add DownloadProgressChanged and Completed events.
    downloader.addEventListener("downloadProgressChanged", onDownloadProgressChanged);
    downloader.addEventListener("completed", onCompleted);
    // Initialize the Downloader request.
    // NOTE: downloader APIs disallow file:\\ scheme
    // you must run this sample over localhost: or off a server or the following call will fail
    downloader.open("GET", "promo.png");
    // Execute the Downloader request.
    downloader.send();
}

Defining Downloader Event Handlers

The following table shows the events provided by the Downloader object.

Event Description
Completed Occurs when the download request has completed and returned content.
DownloadFailed Occurs when the download request has failed to return content.
DownloadProgressChanged Occurs while content is being downloaded during a download request.

Handling the DownloadProgressChanged Event

The DownloadProgressChanged event is often used with a visual progress indicator, which displays the percentage of content that has been downloaded. A visual progress indicator can be a simple set of XAML content, such as Rectangle and TextBlock objects, as shown in the following example.

XAML
<!-- Visual progress indicator -->
<Canvas Canvas.Top="70">
  <Rectangle
    x:Name="progressRectangle"
    Canvas.Left="20"
    Height="10" Width="0"
    Fill="Maroon" />
  <Rectangle
    Canvas.Top ="-1"
    Canvas.Left="19" Height="12"
    Width="202"
    StrokeThickness="1" Stroke="Black" />
  <TextBlock
    x:Name="progressText"
    Canvas.Top ="-4" Canvas.Left="230"
    Text="0%" FontSize="12" />
</Canvas>

The following JavaScript example shows how to define a DownloadProgressChanged event-handler function that updates the visual progress indicator shown in the preceding XAML.

JavaScript
// Event handler for updating visual progress indicator
function onDownloadProgressChanged(sender, eventArgs)
{
    // Calculate the downloaded percentage.
    var percentage = Math.floor(sender.downloadProgress * 100);
    // Update the Rectangle and TextBlock objects of the visual progress indicator.
    progressText.text = percentage + "%";
    progressRectangle.width = percentage * 2; 
}

The following illustration shows the visual progress indicator at the starting, middle, and ending position of the download.

Visual progress indicator

Visual progress indicator

A visual progress indicator can be constructed from a wide variety of XAML content, including animated objects. The progress is reported by the DownloadProgress property as a Double value between 0 and 1.

Defining a Completed Event

The Completed event occurs when the download request has completed and has returned content. Once the Completed event is raised, you can use either the GetResponseText method or the ResponseText property to access the content. The following JavaScript example shows how to define a Completed event-handler function that accesses the downloaded content.

JavaScript
// Event handler for the Completed event.
function onCompleted(sender, eventArgs)
{
    // Retrieve downloaded XAML content.
    var xamlFragment = sender.ResponseText;
    // Create the objects from the XAML content.
    var plugin = sender.getHost();
    var button = plugin.content.createFromXaml(xamlFragment);
    // Add downloaded XAML content to the root Canvas of the plug-in.
    var rootCanvas = sender.findName("rootCanvas");
    rootCanvas.children.add(button);
}

Retrieving Downloaded Content

When a Downloader object request is completed, the value from the ResponseText property represents the downloaded content as a string value. The response text can represent a plain string, XAML content, JavaScript content, or a media asset such as an image.

Note   If you are downloading media assets for use by the Source property of Image and MediaElement objects, you can also use the SetSource (Image) or SetSource (MediaElement) methods. This is a more efficient way to transfer large media assets. ImageBrush also has a SetSource method, and TextBlock has SetFontSource (for collections of fonts).

Using the URI Property to Identify Downloaded Content

When you invoke the Send method of the Downloader object, you specify a uri parameter that represents the data to be downloaded. The URI property corresponds to the value of the uri parameter of the Send method. The URI property enables you to differentiate downloaded content when you use a common DownloadProgressChanged or Completed event-handler function for multiple Downloader object requests (alternatively, you could differentiate requests by creating separate Downloader instances and event handlers).

The following JavaScript example shows how to use the URI property in a Completed event-handler function to determine what actions to take for the returned value of the GetResponseText method invocations. Because the downloaded content does not represent packaged content, the part parameter for GetResponseText must be set to an empty string.

JavaScript
// Event handler for the Completed event.
function onCompleted(sender, eventArgs)
{
    if (sender.uri == "OK_button.js")
    {
        // Evaluate JavaScript code -- causes code to be memory-resident.
        eval(sender.ResponseText);
    }
    if (sender.uri == "OK_button.xaml")
    {
        // Retrieve downloaded XAML content.
        var xamlFragment = sender.ResponseText;
        // Create the objects from the XAML content.
        var plugin = sender.getHost();
        var button = plugin.content.createFromXaml(xamlFragment);
        // Add downloaded XAML content to the root Canvas of the plug-in.
        var rootCanvas = sender.findName("rootCanvas");
        rootCanvas.children.add(button);
    }
}

Retrieving Content from Packages

Instead of returning a string that you process as a single entity, the Downloader can also return a package, which is in the form of a .zip file. To retrieve the content, you write a handler for the Completed event. Instead of using the ResponseText property to retrieve contents, you use the GetResponseText method. GetResponseText has a part parameter, which you specify as the name of the part you want to retrieve from within the package.

Using the CreateFromXamlDownloader Method to Create XAML Content

The CreateFromXamlDownloader method creates XAML content dynamically by using the downloaded content in the Downloader object. If successful, the CreateFromXamlDownloader method returns an object reference, which you can then add to the existing Silverlight object hierarchy. You can create a single Silverlight object, such as a TextBlock, or an entire tree of Silverlight objects.

The CreateFromXamlDownloader method is an efficient mechanism for creating XAML objects. In addition to performing the same function as GetResponseText and CreateFromXaml called in sequence, it avoids having to copy downloaded content from the GetResponseText method return value to a temporary buffer and then back again for use by the XAML parser. However, one limitation is that CreateFromXamlDownloader does not expose the CreateFromXaml parameter createNameScope that enables creating the new object with a disconnected namescope. If you need to create objects with a disconnected namescope, you should use GetResponseText and CreateFromXaml called in sequence. For details on why creating a disconnected namescope is sometimes desirable, see XAML Namescopes.

The following JavaScript example shows how to add XAML content to an existing Canvas object by using the CreateFromXamlDownloader method. Because the downloaded content does not represent packaged content, the part parameter for CreateFromXamlDownloader must be set to an empty string.

JavaScript
// Event handler for the Completed event.
function onCompleted(sender, eventArgs)
{
    // Retrieve a reference to the plug-in.
    var slPlugin = sender.getHost();
    // Retrieve the XAML fragment and create an object reference.
    // In this case, since the downloaded content represents a single file, OK_button.xaml,
    // the part parameter is set to an empty string.
    var xamlFragment = slPlugin.content.createFromXamlDownloader(sender, "");
    // Add the XAML object as a child of the root Canvas object.
    var root = sender.findName("rootCanvas");
    root.children.add(xamlFragment);
}

XAML content that is created by using the CreateFromXamlDownloader method is not rendered until it is added to an object by using the Add method. The following illustration shows the relationship between the Silverlight object hierarchy and the downloaded XAML content before and after the content is added to the object hierarchy.

Silverlight object hierarchy and downloaded XAML content

Silverlight object hierarchy and downloaded XAML content

You can also use the CreateFromXamlDownloader method to retrieve a specific part within the downloaded content package. When the downloaded content package is a .zip file, the CreateFromXamlDownloader method enables you to retrieve the contents of a file name within the .zip file. The following JavaScript example shows how to use the CreateFromXamlDownloader method, using the part parameter to reference a specified part within the downloaded content.

JavaScript
// Event handler for the Completed event.
function onCompleted(sender, eventArgs)
{
    // Retrieve a reference to the plug-in.
    var slPlugin = sender.getHost();
    // Retrieve the specified XAML file from the packaged downloader content,
    // and create an object reference.
    var xamlFragment = slPlugin.content.createFromXamlDownloader(sender, "OK_button.xaml");
    // Add the XAML object as a child of the root Canvas object.
    var root = sender.findName("rootCanvas");
    root.children.add(xamlFragment);
}

Using the SetSource Method to Set Media Content

The SetSource method can be used as a way of setting downloaded media content to the Source property of the MediaElement object. Equivalent methods also exist on Image and ImageBrush. The first parameter of the SetSource method identifies the Downloader object representing the downloaded content. The second parameter identifies the specific part to retrieve within the downloaded content. If the downloaded content does not represent packaged content, such as a .zip file, the second parameter must be set to an empty string. The following JavaScript example shows how to use the SetSource method to set the Source property of an Image object to the downloaded content.

JavaScript
// Event handler for the Completed event.
function onCompleted(sender, eventArgs)
{
    // Retrieve the Image object.
    var myImage = sender.findName("myImage");
    // Set the Source property to the contents of the downloaded object,
    // In this case, since the downloaded content represents a single image file, promo.png,
    // the part parameter is set to an empty string.
    myImage.setSource(sender, "");
}

You can also use the SetSource method to retrieve a specific part within the downloaded content package. When the downloaded content package is a .zip file, the SetSource method enables you to retrieve the contents of a file name within the .zip file. The following JavaScript example shows how to use the SetSource method to set the Source property of an Image object to a specified part within the downloaded content.

JavaScript
function onDownloadCompleted(sender, eventArgs)
{
    // Retrieve the XAML content from the downloaded package file.
    var jacketBrowserXaml = sender.getResponseText("jacketBrowser.xaml");
    // Create the objects from the XAML content.
    var jacketBrowser = plugin.content.createFromXaml(jacketBrowserXaml);
    // Add downloaded XAML content to the root Canvas of the plug-in.
    sender.findName("root").children.insert(0, jacketBrowser);
    // Retrieve a reference to the Image object representing the jacket.
    var jacketImageSlice = sender.findName("jacketSlice");
    // Set the Source property of the Image object to the specific jacket image
    // within the downloaded Zip package file.
    jacketImageSlice.setSource(sender, "rotation01_green.png");
}

Using SetFontSource for Downloaded Fonts

The SetFontSource method can be used to add downloaded font content to the existing collection of typefaces for a TextBlock object. The downloader parameter identifies the Downloader object that represents the downloaded content. The downloaded content can either be an individual font file or packaged content, such as a .zip file, that contains one or more font files.

Note   Font files used with the SetFontSource method must be OpenType or TrueType fonts, and must have a file name extension of .ttf.

The following JavaScript example shows how to use the Downloader object to download an individual font file.

JavaScript
// Event handler for initializing and executing a font file download request.
function onMouseLeftButtonUp(sender, eventArgs)
{
    // Retrieve a reference to the plug-in.
    var plugin = sender.getHost();
    // Create a Downloader object.
    var downloader = plugin.createObject("downloader");
    // Add Completed event.
    downloader.addEventListener("Completed", onCompleted);
    // Initialize the Downloader request.
    downloader.open("GET", "SHOWG.TTF");
    // Execute the Downloader request.
    // NOTE: downloader APIs disallow file:\\ scheme
    // you must run this sample over localhost: or off a server or the following call will fail
    downloader.send();
}

When the font file has been downloaded, it needs to be added to the TextBlock's collection of typefaces. When it has been added to the collection, it can then be selected by using the FontFamily property. The following JavaScript example shows how to use the SetFontSource method to add the the font file to the typeface collection, and then set the FontFamily property to display the TextBlock with the downloaded font.

JavaScript
// Event handler for the Completed event.
function onCompleted(sender, eventArgs)
{
    // Retrieve the TextBlock object.
    var myTextBlock = sender.findName("myTextBlock");
    // Add the font files in the downloaded object to the TextBlock's type face collection.
    myTextBlock.setFontSource(sender);
    // Set the FontFamily property to the friendly name of the font.
    myTextBlock.fontFamily = "Showcard Gothic";
    myTextBlock.text = "Showcard Gothic";
}

You can also download font files contained in a packaged format, such as a .zip file. The .zip file can contain other content files, such as image files.

Contents of a .zip file

Contents of a .zip file

The following JavaScript example shows how to use the Downloader object to download a .zip file that contains an image file and multiple font files.

JavaScript
// Initialize the Downloader request. Zip file contains: Coco.png, Britanic.ttf, Erasbd.ttf, Showg.ttf
downloader.open("GET", "myMediaAssets.zip");

When the .zip file has been downloaded, the font files it contains must be added to the collection of typefaces for the TextBlock. Once the font files have been added to the collection, they can then be selected by using the FontFamily property. The following JavaScript example shows how to use the SetSource and SetFontSource methods to use the downloaded content.

JavaScript
// Event handler for the Completed event.
function onCompleted(sender, eventArgs)
{
    // Retrieve the Image object.
    var myImage = sender.findName("myImage");
    // Set the Source property of the Image object to the specific image
    // within the downloaded Zip package file.
    myImage.setSource(sender, "Coco.png");
    // Retrieve the TextBlock object.
    var myTextBlock = sender.findName("myTextBlock");
    // Add the font files in the downloaded package object to the TextBlock's type face collection.
    myTextBlock.setFontSource(sender);
    // Set the FontFamily property to the friendly name of the font.
    myTextBlock.fontFamily = "Showcard Gothic";
}

To return to the default font used to display the TextBlock, set the downloader parameter of the SetFontSource method to null. The following JavaScript example shows how to use the SetFontSource method with a null parameter value.

JavaScript
// Retrieve the TextBlock object.
var myTextBlock = sender.findName("myTextBlock");
// Remove the custom font setting.
myTextBlock.setFontSource(null);

Handling Downloader Errors

There are two mutually exclusive events that report success or failure of a download. These are Completed (success) and DownloadFailed (failure). Typically you write handlers for both events.

The Status and StatusText properties provide the HTTP status code and corresponding text string value that represent the final state of the Downloader object request. Generally speaking, status will always be 200 (success) from the Completed handler and 204 (no content) from the DownloadFailed handler.

Using the Abort Method

The Abort method cancels the download request that has been executed by using the Send method of the Downloader object. In addition, the Abort method resets the following Downloader properties to their initial state.

Property name Initial state
DownloadProgress 0
Status 0
StatusText Empty string

The following JavaScript example shows how to invoke the Abort method.

JavaScript
// Cancel download on mouse click.
function onLeftMouseButtonUp(sender, eventArgs)
{
    // Cancel Downloader activity and restore state.
    downloader.abort();
}

When you call Abort, this does not raise the DownloadFailed event (or the Completed event).

URI Protocols and Schemes

The URI that you specify for the first parameter of the Open method will generally use relative URIs. The relative URI will then use the same URI scheme that is used to serve the HTML page, which is generally HTTP. The starting location for the relative reference is the HTML page that contains the current Silverlight plug-in. Setting a cross-domain URI for the second parameter of the Open method is not permitted, and such download attempts will fail. In this case, DownloadFailed does not occur. Instead, a method error is thrown on the Send method call.

Because of the cross-domain restrictions on Downloader, if you are setting media or images using URI sources that are cross-domain, do not use the various SetSource methods, because these require a Downloader to process the URI. Instead, set the respective Source properties directly, and specify the URI as a string.

The Downloader does not support downloads through the FILE scheme. This may be an issue if you are testing Silverlight-based applications locally in the file system, instead of developing and then deploying your Web site to a test server or to localhost. The downloader will fail against the FILE scheme at the point when you call the Send method.

You can use HTTPS as the implied protocol for downloads, but only if the HTML page that contains the current Silverlight plug-in is also HTTPS.

Backslashes (\) are not permitted in Downloader URIs; always use forward slashes (/).

For more information on Silverlight URI restrictions for Downloader as well as for other APIs, see Silverlight URL Access Policy.

See Also

Downloader
CreateFromXamlDownloader
CreateObject
SetFontSource
SetSource (MediaElement)
SetSource (Image)
Overviews and How-to Topics