
Creating and Initializing a WebClient Object
In order to use the WebClient class in your Silverlight-based applications, you have to add a reference to the System.Net assembly (System.Net.dll) to your application project, and add the following using statement to your code-behind file.
To create a WebClient object, call its constructor.
Because the WebClient requests are asynchronous, most of the interaction that your code has with the WebClient will be through event handlers. Typically this involves defining event-handlers for one or more of the following events:
When you initiate a request, you use different APIs depending on whether you are requesting a resource as a stream, or as a string.
To request a resource as a stream, you first call one of the following overloads of OpenReadAsync:
OpenReadAsync(Uri)
OpenReadAsync(Uri, Object)
Next, you handle the OpenReadCompleted event.
To request a resource as a string, you call one of the following overloads of DownloadStringAsync:
Next, you handle the DownloadStringCompleted event.
Important Note: |
|---|
You can only initiate one request at a time with
WebClient APIs. If you make a second request before the first request raises its completed event or raises an asynchronous error (in other words, while IsBusy is true), the second request will cause a NotSupportedException.
|
Whether to download content as a stream or a string depends on what APIs you intend to call on the content result after you download it, and how the content is used. In general, downloading as a stream will probably be more common, because streams are used in the APIs for several dynamic UI scenarios, such as providing image sources, fonts, or MediaElement content on demand. Also, you can use streams to access parts of a downloaded package.
OpenReadCompleted Handler
The following is the signature of an OpenReadCompleted event handler.
void wc_OpenReadCompleted(object sender, OpenReadCompletedEventArgs e) {
...
}
The most relevant API in this handler is the Result of the OpenReadCompletedEventArgs. The Result is of type Stream. You can use this stream directly to set properties or call methods that can take a stream, for example SetSource. Alternatively, you can use a StreamReader or other APIs to access the stream.
DownloadStringCompleted Handler
The following is the signature of an DownloadStringCompleted event handler.
void wc_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e) {
...
}
The most relevant API in this handler is the Result of the DownloadStringCompletedEventArgs. The Result is of type String. You can use this string directly to set properties or call methods that can take a string, for example Load. Or you can perform your own processing of the string.
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. If you are downloading a package, you call OpenReadAsync(Uri), so that the returned type is a Stream.
Obtaining named parts from the package is not done by the WebClient class APIs directly. Instead, you use StreamResourceInfo to process the stream as a package, and then return one of the parts of the package as a new stream.
Using the SetSource Method
Several media types can have their source properties set asynchronously. You first get a specific stream that contains the source material as described previously. Then, call one of the SetSource methods that use the stream as the source for an object's media. The following is a list of SetSource methods that work this way:
The SetSource can be used as a common source for either Image..::.Source or ImageBrush..::.ImageSource. This enables asynchronous source-setting for either Image or ImageBrush.
You can also set asynchronous sources for a VideoBrush. You would first set the source for the MediaElement that is used as the video source with SetSource. Then, call SetSource.
For example, a package might contain a graphics file that is used for an Image source. In this case, a part reference is the file name of the individual content within the package.
The following C# example shows how to use the SetSource method to populate an ImageSource with a specific part stream from the downloaded content. Then, the ImageSource is used to used to set the Source property of an Image object.
The first code example establishes the WebClient for the request, adds the completed handler, and initiates the request. Note that it passes a token value imgPart. This will inform the handler of which part is desired from the overall package.
void DownloadImagePart(string imgPart)
{
WebClient wc = new WebClient();
wc.OpenReadCompleted += new OpenReadCompletedEventHandler(wc_OpenReadCompleted);
wc.OpenReadAsync(new Uri("imgs.zip", UriKind.Relative), imgPart);
}
The next code example defines the completed handler referenced in the previous code. Within the completed handler, the initial stream is processed to get a specific part, and that part is used to set the image source. The part name itself was passed as a token, retrieved from e.UserState, and used to specify the URI within the stream/package. ImgToFill in this code example is a reference to an Image that was defined in XAML with x:Name of ImgToFill (not shown).
void wc_OpenReadCompleted(object sender, OpenReadCompletedEventArgs e)
{
StreamResourceInfo sri = new StreamResourceInfo(e.Result as Stream, null);
String sURI = e.UserState as String;
StreamResourceInfo imageStream = Application.GetResourceStream(sri, new Uri(sURI, UriKind.Relative));
BitmapImage imgsrc = new BitmapImage();
imgsrc.SetSource(imageStream.Stream);
ImgToFill.Source = imgsrc;
}
To use the APIs listed in the previous sample, you have to add the following using statements to your file.
using System.IO;
using System.Windows.Resources;
Also, if you are setting image sources from download parts, BitmapImage is in a separate namespace from the rest of the image and media APIs, so also include the following namespace:
using System.Windows.Media.Imaging;
FontSource
You can also retrieve fonts from downloaded packages, which are added to the font source collection for text display elements, such as TextBox and TextBlock. The mechanism for getting fonts for a font source is slightly different. You request the package that contains the fonts as a stream (by using OpenReadAsync(Uri)). After handling OpenReadCompleted, retain a reference to the stream. Use the stream as the constructor parameter for FontSource (FontSource constructor). All font sources in the package are available and are added to the font collection in the FontSource. You do not have to specify a part by name. Constructing a FontSource turns the parts into collection items, with the font name / part name becoming the key for the font source of an individual font in the collection. Then you set FontSource properties, such as FontSource, with the FontSource object. You specify specific font names when you set the FontFamily properties on various types and the corresponding font is obtained from the font source collection.
Note: |
|---|
You can retrieve fonts from packages that contain non-font parts. Only the font source parts are used for the
FontSource.
|
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.
<!-- Visual progress indicator -->
<Canvas Canvas.Top="70">
<Rectangle
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>
To add the handler for progress, add it at the same time that you add either the OpenReadCompleted or DownloadStringCompleted handlers.
void DownloadImagePart(string imgPart)
{
WebClient wc = new WebClient();
wc.OpenReadCompleted += new OpenReadCompletedEventHandler(wc_OpenReadCompleted);
wc.DownloadProgressChanged += new DownloadProgressChangedEventHandler(wc_DownloadProgressChanged);
wc.OpenReadAsync(new Uri("imgs.zip", UriKind.Relative), imgPart);
}
The following C# example shows how to define a DownloadProgressChanged event-handler function that updates the visual progress indicator shown in the previous XAML.
// Event handler for updating visual progress indicator
void wc_DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e)
{
// Calculate the downloaded percentage.
// Update the Rectangle and TextBlock objects of the visual progress indicator.
progressText.Text = e.ProgressPercentage.ToString();
progressRectangle.Width = (double) e.ProgressPercentage;
}
The following illustration shows the visual progress indicator at the starting, middle, and ending position of the download.
Visual progress indicator
.png)
A visual progress indicator can be constructed from a wide variety of XAML content, such as animated objects. The progress is reported by the ProgressPercentage property as a Double value between 0 and 100.
Note: |
|---|
If you are migrating
Silverlight version 1.0 code, the equivalent Downloader progress properties returned a double between 0 and 1, so you may have to eliminate any *100 logic you used to get a percentage number between 0 and 100.
|
Using the CancelAsync Method
You might call CancelAsync in response to your own timers, or you might provide a UI option so that the user can cancel any download. Before calling CancelAsync you should check IsBusy to make sure that you are not about to cancel a request that just completed. The following code shows how to perform this check.
void CancelButton_ClickHandler(object sender, RoutedEventArgs e)
{
// Check if async download is in progress
if (wc.IsBusy)
{
// Initiate cancel
wc.CancelAsync();
}
}
URI Protocols and Schemes
The URI that you specify for the OpenReadAsync and DownloadStringAsync APIs 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, also known as the site of origin.
You can download cross-domain content using WebClient APIs from the client perspective. But the server that is serving cross-domain must have a client access policy that allows cross-domain access. For more information, see Make a Service Available Across Domain Boundaries.
WebClient 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.
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 URIs; always use forward slashes (/).