Downloading content
You’ll often want to pull down content, be it XAML, images or media. In many cases you can simply set the Source property of the relevant control or element, but what happens if you want some finer-grained control over the downloading process? Fortunately, Silverlight includes a Downloader object to do the heavy lifting for you. Let’s take a quick look at how you might use it to download a video, in this case whenever the user clicks the Load button.
We’re aiming for a nice (at least to the limits of my graphical capabilities) download bar, as shown in Figure 2.
Figure 2: A simple progress bar
To achieve this effect we can show a Rectangle and adjust its Width as the download progresses. The code for this is shown in the following listing:
<Canvas ...> ... <MediaElement x:Name=”thePlayer” ... /> <Canvas x:Name=”progressBar” Visibility=”Hidden” Canvas.Top=”413” Canvas.Left=”272”> <Rectangle Stroke=”#FF0A358C” StrokeThickness=”2” Width=”204” Height=”30” RadiusX=”10” RadiusY=”10” /> <Rectangle Width=”0” Canvas.Top=”2” Height=”28” x:Name=”progressRect” Canvas.Left=”2” RadiusX=”8” RadiusY=”8”> <Rectangle.Fill> <LinearGradientBrush StartPoint=”0,0” EndPoint=”1,0”> <GradientStop Color=”Blue” Offset=”0”/> <GradientStop Color=”Purple” Offset=”1”/> </LinearGradientBrush> </Rectangle.Fill> </Rectangle> </Canvas> </Canvas>
The progress bar consists of a Canvas that contains a couple of Rectangles that will be used to provide the outline and the fill of the bar. To implement the bar, we need to show the canvas and then adjust the width of the filled rectangle to reflect the download progress. All that is required once the media file has been downloaded is to hide the Canvas.
The key to the whole download mechanism is the Downloader object. This both performs the download and raises events to indicate its progress and to indicate that the download is complete. This is clearly very similar to making an XmlHttpRequest download in AJAX.
The full code to perform the media download is shown below:
handleMouseUp: function( sender, eventArgs) { var downloader = sender.getHost(). createObject( “downloader” ); downloader.addEventListener( “downloadProgressChanged”, Sys.Silverlight.createDelegate( this, this.onProgressChanged ) ); downloader.addEventListener( “completed”, Sys.Silverlight.createDelegate( this, this.onDownloadCompleted ) ); sender.findName( “progressBar” ).visibility = “Visible”; downloader.open( “GET”, “http://localhost/mediasite/ handler.ashx?movie=brushes”, true ); downloader.send(); }, onProgressChanged: function( sender, eventArgs ) { var progressRect = sender.findName( “progressRect” ); if( sender.downloadProgress <= 1.0 ) progressRect.width = sender.downloadProgress * 200.0; }, onDownloadCompleted: function( sender, eventArgs ) { sender.findName( “progressBar” ). visibility = “hidden”; sender.findName( “progressRect” ). width = 0.0; sender.findName( “thePlayer” ). setSource( sender, “” ); }, ...
The handleMouseUp method is called in response to the user clicking the Load button. This method uses the Silverlight control’s createObject() method to create the Downloader object. In fact, in Silverlight 1.0 this is the only type of object that you can create with createObject().
We then connect up the two main events of the Downloader so that we can report progress, before commencing the download. One thing to be aware of is that the Visibility property of an element is not a Boolean, but one of three values: hidden, visible or collapsed.
In the onProgressChanged event handler the width of the progress rectangle is changed to reflect the percentage that’s been downloaded. There’s only one thing to watch out for here: the downloadProgress value returned by the Downloader object will be in the range 0 to 1, unless the content-length header has not been set by the server. In this case, it will return infinity, which is not a legal value for the width of a rectangle!
Finally, in the onDownloadCompleted event handler we hide the Canvas that contains all the progress bar information and then set the Source property on our MediaElement. The setSource() method takes a reference to the Downloader, plus the name of a the part that should be used, which should be set to “” when only a single file (rather than a .zip file) has been downloaded.
As you can appreciate, the Downloader object offers functionality that’s very similar to making a standard AJAX callback.
Dynamic content
One of the joys of Silverlight is the way that you can dynamically create and alter content. Again, this is something that’s being done increasingly with AJAX applications, but I find that Silverlight is just that little bit easier.
Let’s start with a simple client-only example. Figure 3 shows an updated version of the page, which now includes a facility.
Figure 3: Dynamically loading XAML
This is perhaps not the sort of feature that you’d want to provide in a typical media player, but it certainly shows how flexible the XAML scene can become.
<script type=”text/javascript”> <!-- function loadXAML() { var control = document.getElementById( “SilverlightControl” ); var xamlFragment = document.getElementById( “txtXAML” ).value; var elem = control.content.createFromXAML( xamlFragment ); var btn = control.content.findName( document.getElementById( “selectTarget”).value ); btn.fill = elem; } // --> </script> ... <textarea id=”txtXAML” ... /> <select id=”selectTarget”>...</select> <input id=”btnLoadXAML” type=”button” value=”Load” onclick=”loadXAML();” /> ...
As you can see, the code in the listing merely extracts the value from the textarea and uses it to create some elements from the XAML, which it then sets as the fill for the selected button. Clearly, this assumes that the user enters valid XAML for a Brush, such as a SolidColorBrush or LinearGradientBrush.
Note that it will rarely make sense to allow the user to enter XAML directly, but the purpose of this code was really to show you how to create XAML on the client and add it into the current scene.
Comments