Chapter 8. Downloading Content on Demand

IN THIS CHAPTER

Silverlight includes a special downloader object that can fetch all sorts of content from your web server, such as XAML files, JavaScript files, font files, images, videos, or even all these inside a .ZIP file. This enables you to easily delay the retrieval of content until it is needed, which helps you create a more responsive user experience. Or, if nothing else, you can provide a sexy progress indicator while all the content downloads rather than relying on the default browser experience.

The downloader issues HTTP GET requests that don’t refresh the current web page, much like the XmlHttpRequest object that has become the foundation for Asynchronous JavaScript and XML (AJAX). If you’ve used the XmlHttpRequest object that browsers provide to JavaScript, using the Silverlight downloader will be a familiar experience because it was modeled after the object.

Initiating a Download

To initiate a download, you must create the downloader object with a call to the Silverlight control’s CreateObject function, optionally attach some event handlers, and call the downloader’s Open and Send functions. The following JavaScript demonstrates how to initiate a download to a secondary XAML file (MoreXaml.xaml) inside the onLoad event handler for the primary XAML content:

image

image

For Silverlight 1.0, "downloader" (case-insensitive) is the only valid parameter for CreateObject. Future versions of Silverlight might include additional objects you can create with this mechanism. The downloader defines a Completed event that will be raised when the download successfully completes. This code uses the AddEventListener function discussed in the preceding chapter to attach an onCompleted handler defined in the next section.

The downloader’s Open and Send functions are simplified versions of XmlHttpRequest’s open and send functions. The first parameter must always be set to the string "GET", referring to the HTTP verb GET. The second parameter is the URL of the file you want to download, relative to the HTML page hosting the JavaScript. The Send function sends the HTTP GET request, which is always performed asynchronously. That is why you should listen for the Completed event if you want to perform an action as soon as the file download is complete. The downloader also has an Abort function for stopping the download, so you can support a user interface with a cancel button.


Warning

The URL passed to the downloader’s Open function has several limitations!

The URL passed to Open must be a relative URL, although you could always start it with a forward slash if you want to reference something relative to the domain root. The result of this limitation is that you can only use the Silverlight downloader to download content from the same domain (and same protocol) that served the current web page. This is consistent with the policy that browsers enforce with the XmlHttpRequest object and Silverlight enforces for its XAML source, although these other cases do support absolute URLs as long as the domain matches.

Also, a web page sitting on your local hard drive can’t use the downloader to retrieve content via normal file system paths. Instead, you must host the content on a web server (even if it is just localhost).


Using the Downloaded Content

When the download is complete, your Completed event handler is called (if you attached one), and the downloader itself is passed as the first parameter. The downloader has a property called ResponseText that contains the actual downloaded content represented as a string. (In the preceding example, that would be the content of MoreXaml.xaml.)

If the control’s current XAML contained a Canvas as the root element, and if you wanted to add the entire downloaded XAML content as a new child to this Canvas, you could use the following implementation of a Completed event handler:

image

The downloader object has a Status property (and corresponding StatusText property) that represents the HTTP status code returned from the underlying HTTP GET request. Because the Completed event handler is only called after a successful download, there are only two expected status codes: 200 (with StatusText set to “OK”) and 204 (with StatusText set to “No content”). The downloader also has a URI property set to whatever URL was passed to the Open call. This is convenient for determining which download has completed if you’re handling more than one from the same event handler.

As with any network request, a download might fail unexpectedly. You can attach a handler to the downloader’s DownloadFailed event and handle the failure in a custom way. DownloadFailed event handlers are passed the downloader as the sender and the same errorEventArgs parameter passed to the ImageFailed event mentioned in Chapter 5, “Brushes and Images.” By default, the error would be handled by the default onError event handler attached to the control (such as the default_error_handler function discussed in Chapter 1, “Getting Started”).


Tip

Although you can parse and load downloaded XAML in two easy steps inside a Completed event handler (retrieving the ResponseText and then sending it to CreateFromXaml), the Silverlight control’s Content property defines another XAML-parsing function optimized specifically for downloaded XAML. This function is called CreateFromXamlDownloader, and it accepts the entire downloader object rather than a string. Therefore, instead of writing the following code inside a Completed event handler

image

you should write the following:

var newContent = sender.GetHost().Content.CreateFromXamlDownloader(sender, "");

This is more efficient than using CreateFromXaml because it avoids copying the XAML content into a temporary string. The second parameter to CreateFromXamlDownloader is only relevant for downloading packages, which are described in the next section.


When downloading binary content (font files, images, or videos), you shouldn’t use the ResponseText property on the downloader object. Instead, much like the CreateFromXamlDownloader mechanism, UI elements that know how to display text (TextBlock), images (Image and ImageBrush), and videos (MediaElement) define a SetSource (or SetFontSource) function that accepts the downloader object and a part name. Chapter 4, “Text,” demonstrated this with the TextBlock element.


Tip

For the best performance, you should detach all downloader event handlers and then set the downloader instance to null when you’re done with it (inside the Completed event handler, for example).


Downloading Multiple Items Simultaneously in a .ZIP File

The downloader supports retrieving a package containing multiple parts and (most importantly) selectively retrieving those parts after the download has finished. You can take advantage of this package support by compressing your files into a .ZIP file (using a tool such as WinZip or the compressed folder functionality in Windows).

Initiating a download of a .ZIP file is no different than initiating a download of any other file; simply give the appropriate URL to the downloader’s Open function. The difference is in the consumption of the downloaded data. Rather than using the ResponseText property to retrieve the downloaded content, you should call the downloader’s GetResponseText function. This accepts a “part name” parameter, enabling you to specify which part of the package you want to retrieve. For .ZIP files, the “part name” is simply the filename inside the .ZIP file. Therefore, if you downloaded a .ZIP file containing two XAML files and two JavaScript files, you could retrieve them inside a Completed event handler as follows:

image

image

The second parameter of CreateFromXamlDownloader is the same “part name” you can give to GetResponseText (or an empty string if you didn’t download a package), so you can call it as follows to efficiently parse and load XAML that was downloaded inside a .ZIP file:

image

The various SetSource and SetFontSource functions also accept a “part name” parameter. Therefore, to use binary content inside a .ZIP file, be sure to pass the filename as the second parameter.

Silverlight handles uncompressing the content, so the efficiency gained by combining and compressing multiple files in a single .ZIP file is practically abstracted away to your JavaScript code. (This is crucial because the ability to download a .ZIP file wouldn’t be very interesting if you didn’t have a way to uncompress it!)


Tip

Your .ZIP file can have any file extension and still work with the Silverlight downloader, unless it contains font files. Downloaded fonts inside a package can only be applied to a TextBlock if the package extension is .ZIP. This is simply a bug in Silverlight 1.0.


Displaying a Progress Bar

Because the downloader does its work asynchronously, you are free to show some sort of “loading” user interface while users wait for the download to complete. These days, graphics that make no commitment about how much progress remains are pretty popular, such as the spinning blue wait cursor in Windows Vista or the series of pulsing dots on websites such as expedia.com. You could certainly show such a graphic (sometimes called an indeterminate progress bar) after calling Send, and then hide it after your Completed event handler is called. But Silverlight makes it possible for you to go a step further and provide a determinate progress bar, thanks to an event and a property that tells you exactly how much of the download remains.

This event is called DownloadProgressChanged, and it is called throughout the download process, whenever progress changes by at least .05%. The downloader’s relevant property is called DownloadProgress, which is a number between 0 and 1—where 0 means that no progress has been made and 1 means that the download is finished.

A Simple Progress Bar

The following two listings take advantage of the DownloadProgressChanged event to show a custom progress bar before the main XAML content is loaded and rendered. Listing 8.1 contains the initial “loading” user interface with a simple progress bar. Listing 8.2 contains the code needed to download content, update the progress bar, and then replace the UI with the downloaded XAML when complete.

Listing 8.1 Loading.xaml—The Initial “Loading” User Interface


image


Listing 8.2 Code to Handle the Download and Update the Progress Bar


image

image

image


The progress bar is created with two Rectangles: a static one for the background and a dynamically changing one for the foreground. A TextBlock is also used to show the percentage complete in a numeric fashion. The two elements that need to be updated from JavaScript are given names.

In the corresponding JavaScript, the handler for the DownloadProgressChanged event is pretty simple thanks to the downloader’s DownloadProgress property. The downloader is passed as the first parameter to this handler, just as with the Completed event. Because the progress bar’s width is 100, multiplying the DownloadProgress value by 100 not only gives the percent complete, but also the desired width of the progressBar Rectangle. (Math.floor is used to round down the potentially fractional value when displayed as text, but this value is fine as is for Width.)

The onCompleted handler retrieves, parses, and loads the XAML content inside the .ZIP file (assumed to be in a file called Main.xaml), and then it replaces the progress bar with the “real” user interface in two easy steps. First, it clears all children from the root Canvas, and then it adds the new content (which must have a single root of its own) as a single child to the existing Canvas.

The result of running this code is demonstrated with a few snapshots in Figure 8.1.

Figure 8.1 The progress bar updates as the content gets downloaded.

image

If you use the technique from Chapter 5 to get a gradient with a crisp line, you could accomplish the same visual effect from Figure 8.1 with only one Rectangle rather than two. The idea is to adjust the Offset of GradientStops as progress is made. The following XAML accomplishes this:

image

image

if it is used with Listing 8.2 and the following update to onProgressChanged:

image

The crisp line in the gradient, enabled by middleStop1 and middleStop2, moves from left to right as the Offset of these elements is set to the current value of the downloader’s DownloadProgress property. The raw property value can be used directly because gradients operate on the same range of 0 to 1.

Progress Bar Customizations

The progress bar from Figure 8.1 is very plain and simple, but you can use the same techniques to create really innovative progress indicators that match the design of your site. For example, the following XAML can be used with Listing 8.2 and the updated implementation of onProgressChanged that updates the two GradientStop Offsets:

image

image

By switching the Rectangle to an Ellipse and the LinearGradientBrush to a RadialGradientBrush (with different colors), you get a vastly different effect from Figure 8.1, shown in Figure 8.2.

Figure 8.2 The customized progress bar updates as the content gets downloaded.

image

Depending on the effect you want, you could remove the extra GradientStops that enable the crisp line:

image

and update a single Offset with the current DownloadProgress value:

image

image

This produces the result in Figure 8.3.

Figure 8.3 Another look for a customized progress bar, created by tweaking the GradientStops and colors.

image

Another simple idea for a customized progress bar would be to make the dynamic gradient become the Foreground brush of a single TextBlock:

image

The following implementation of onProgressChanged moves the horizontal crisp line in the gradient from top to bottom:

image

The result is shown in Figure 8.4. Note that this example retrieves each GradientStop by calling GetItem on the GradientStops collection rather than via FindName. Silverlight currently has a bug that can cause the setting of a TextBlock’s Text property to fail when its Foreground is either set to a brush with a Name or a brush containing an element (such as a GradientStop) with a Name.

Figure 8.4 The TextBlock’s Foreground serves as the progress bar.

image

Note that starting and ending points of the gradient extend above and below the text that is actually getting displayed. This is a result of the TextBlock leaving room for characters with ascending or descending strokes (such as é or y). Therefore, without additional tweaks, this TextBlock-based progress bar looks almost empty at 25% and completely full at 75%.

Conclusion

Silverlight’s downloader object makes it easy to manage large content effectively. Although using Silverlight’s downloader is optional, it is a good idea to become acquainted with it. Something as simple as downloading content on demand can turn an otherwise unusable application into an application that appears to be extremely responsive. Improvements in raw performance and responsiveness that usually result from using the downloader can be tracked down to several potential factors:

  • Less content is loaded up front.
  • Downloads are potentially smaller than normal HTTP GET requests. (This would be true if you use the .ZIP file support and if your web server doesn’t already compress the content it serves.)
  • Multiple HTTP GET requests can be consolidated into one (if you use the .ZIP file support).
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.17.203.68