Chapter 16. Special Silverlight JavaScript APIs

Advanced JavaScript APIs

This chapter is a mix of the Silverlight JavaScript capabilities introduced in the second part of this book and the JavaScript access to Silverlight content presented in Chapter 15. It features some JavaScript APIs that are part of the Silverlight features but provide advanced possibilities. To be exact, Silverlight exposes a JavaScript API that lets developers download resources. While these resources are being downloaded, you can see the download progress and can process the downloaded data.

Dynamically Downloading Content

The technical background for Silverlight’s downloader API is the XMLHttpRequest JavaScript object, which fuels everything Ajax. The Silverlight API does not copy the XMLHttpRequest API, but provides its own interfaces. You can trigger the API from both XAML code-behind and HTML code-behind JavaScript code, but generally you will want to start from XAML.

When downloading content from within Silverlight, you usually have to follow these steps:

  1. Initialize the downloader object by using the plug-in’s createObject() method.

  2. Add event listeners to the object to react to important events.

  3. Configure the downloader object by providing information about the resource to download (HTTP verb, URL).

  4. Send the HTTP request.

  5. Handle any occurring events.

We will go through these step by step. But first we need some XAML backing so that we can wire up the code. Example 16-1 shows a suggestion.

Example 16-1. Downloading content, the XAML file (Download.xaml)

<Canvas xmlns="http://schemas.microsoft.com/client/2007" 
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Loaded="loadFile">
  <Rectangle Width="300" Height="150" Stroke="Orange" StrokeThickness="15" />
  <TextBlock x:Name="DownloadText" FontSize="52" 
             Canvas.Left="25" Canvas.Top="35" Foreground="Black" 
             Text="loading ..."/>
</Canvas> 

Once the XAML has been loaded (via the Loaded event in the <Canvas> element), we start working on the download. Step 1 is to create the downloader object. For that, we first need to access the plug-in (see Chapter 15 for details) and then call the method:

function loadFile(sender, eventArgs) {
  var plugin = sender.getHost();
  var d = plugin.createObject('downloader'), 

Step 2 is to set up event listeners. Three events are available:

Completed

The download has completed.

DownloadFailed

The download failed—for instance, because the URL does not exist, or there was an internal server error, or security restrictions prevented the download.

DownloadProgressChanged

Another chunk of data has been downloaded from the server; there may be more to come.

For this example, the Completed event is what we want:

  d.addEventListener('completed', showText); 

Step 3 is to configure the downloader object, which we do using the open() method (this does not actually open the connection, but just configures the object). Provide the HTTP verb (usually 'GET’), and the URI to request.

  d.open('GET', 'Download.txt'), 

Step 4 is easy. The send() method does exactly what it says—it sends the HTTP request:

  d.send();

Finally, in step 5, you need to implement the event handlers; in our case, there’s just one. The first argument for that event handler (we call it sender, as always) is conveniently the downloader object. It provides you with two options to access the data returned from the server:

responseText

This property contains the response as a string.

getResponseText(id)

This method returns one specific part of a response. If you download a ZIP file, you can provide the filename inside the archive that you want.

In this example, we use the former option and output the text:

function showText(sender, eventArgs) {
  var textBlock = sender.findName('DownloadText'),
  textBlock.text = sender.responseText;
} 

Now all you need is a file called Download.txt (see the open() method call for the filename) residing in the same directory as your application, and you can download the file. After a (very) short while, you will see the content in the <TextBlock> element. Example 16-2 contains the complete XAML code-behind JavaScript code, and Figure 16-1 shows the output. If you are using a tracing tool such as Firebug (see Figure 16-2), you will see that the text file is actually downloaded.

Example 16-2. Downloading content, the JavaScript file (Download.js)

function loadFile(sender, eventArgs) {
  var plugin = sender.getHost();
  var d = plugin.createObject('downloader'),
  d.addEventListener('completed', showText);  
  d.open('GET', 'Download.txt'),
  d.send();
}

function showText(sender, eventArgs) {
  var textBlock = sender.findName('DownloadText'),
  textBlock.text = sender.responseText;
} 
The content is downloaded

Figure 16-1. The content is downloaded

Firebug unveils the HTTP traffic, including the request to the text file

Figure 16-2. Firebug unveils the HTTP traffic, including the request to the text file

Caution

Downloading works only when you’re using the HTTP protocol. You cannot use the file: protocol (which means that you have to run these examples using a web server, even if it is a local one). You also have to adhere to the same-origin policy of the browser security system. The URL you are requesting must reside on the same domain, use the same port, and use the same protocol.

It’s a good idea to provide the user with information on how long downloading big files will take. The DownloadProgressChanged event is an excellent choice to implement this. We start off again with a simple XAML file (see Example 16-3) that both starts JavaScript code once the canvas has been loaded and includes a text block that will hold the progress information.

Example 16-3. Displaying the download progress, the XAML file (DownloadProgress.xaml)

<Canvas xmlns="http://schemas.microsoft.com/client/2007" 
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Loaded="loadFile">
  <Rectangle Width="300" Height="150" Stroke="Orange" StrokeThickness="15" />
  <TextBlock x:Name="ProgressMeter" FontSize="64" 
             Canvas.Left="60" Canvas.Top="35" Foreground="Black" 
             Text="0 %"/>
</Canvas> 

The best way to simulate a big file is to write a script that does not return that much data, but takes some time to run. The ASP.NET code from Example 16-4 sends only a bit more than 1,000 bytes, but takes a break every few dozen bytes. Therefore, the script is continuously sending data, but taking a few seconds to finish.

Example 16-4. Displaying the download progress, the ASP.NET file simulating a big or slow download (DownloadProgress.aspx)

<%@ Page Language="C#" %>

<script runat="server">
void Page_Load() {
  Response.Clear();
  Response.BufferOutput = false;
  Response.AddHeader("Content-Length", "1003");
  for (int i = 0; i < 17; i++)
  {
    Response.Write(
      "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA");
    System.Threading.Thread.Sleep(500);
  }
  Response.End();
}
</script> 

Caution

It is very important that the script, in whatever language it is written, does not use output buffering and also sends the correct Content-Length HTTP header. Otherwise, Silverlight cannot know how many bytes the server will send in its response, and thus cannot determine the percentage of data that has already been sent.

Now we’re ready for the JavaScript part. The beginning is easy: create a downloader object and send an HTTP request. Only this time, we are listening to the DownloadProgressChanged event:

function loadFile(sender, eventArgs) {
  var plugin = sender.getHost();
  var d = plugin.createObject('downloader'),
  d.addEventListener('DownloadProgressChanged', showProgress);  
  d.open('GET', 'DownloadProgress.aspx'),
  d.send();
} 

The downloader object exposes a property called downloadProgress, which returns a value between 0 and 1, which is a percentage of how much data has already been transferred. When it is multiplied by 100 (and rounded), you get a nice percentage value that can be displayed in the <TextBlock> element. Without further ado, Example 16-5 contains the complete JavaScript code.

Example 16-5. Displaying the download progress, the JavaScript file (DownloadProgress.js)

function loadFile(sender, eventArgs) {
  var plugin = sender.getHost();
  var d = plugin.createObject('downloader'),
  d.addEventListener('DownloadProgressChanged', showProgress);  
  d.open('GET', 'DownloadProgress.aspx'),
  d.send();
}

function showProgress(sender, eventArgs) {
  var progress = sender.downloadProgress;
  var textBlock = sender.findName('ProgressMeter'),
  textBlock.text = Math.round(100 * progress) + ' %';
} 

Figure 16-3 shows the result—the file is loading, and loading, and loading.

The loading file

Figure 16-3. The loading file

Using Additional Fonts

One of the font support limitations mentioned in Chapter 5 is that only a handful of fonts are supported, but at least they work on all supported browsers and operating systems. With the downloader object and some JavaScript code, we can use additional fonts, as long as they are TrueType (TTF) or OpenType fonts. Another limitation: they need to use the .ttf file extension.

Caution

Probably the biggest limitation of them all is a legal one: you have to make the TTF files available to Silverlight so that the application can download them. If the application can download the fonts, any user can. So, you need to make sure you are allowed to distribute the fonts you are using. For this reason, this book’s downloads do not come with the fonts mentioned here—you need to use your own. For registered Visual Studio 2005 users, there is a font download (see Further Reading later) that you might want to experiment with. (But again, you are not licensed to distribute this font to others.)

As usual, we start with a simple XAML file that will be enriched with JavaScript code. Example 16-6 contains a rectangle (for visual reasons) and a <TextBlock>. The text block uses the Lucida font by default (see Chapter 5). We want to change this using JavaScript, therefore we set up an event handler for the Loaded event so that we can load a font once the XAML has been loaded.

Example 16-6. Loading a font, the XAML file (TrueTypeFont.xaml)

<Canvas xmlns="http://schemas.microsoft.com/client/2007" 
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Loaded="loadFont">
  <Rectangle Width="300" Height="150" Stroke="Orange" StrokeThickness="15" />
  <TextBlock x:Name="FancyText" FontSize="64" Canvas.Left="20" Canvas.Top="35" 
             Foreground="Black" Text="Silverlight" />
</Canvas> 

Loading the font starts the same as loading any file using the downloader object—it makes an HTTP request and sets up a Completed event handler. Make sure you have the calibri.ttf (the Calibri font installed as part of Office 2007 and Windows Vista) file available or use another font file and change the code accordingly:

function loadFont(sender, eventArgs) {
  var plugin = sender.getHost();
  var d = plugin.createObject('downloader'),
  d.addEventListener('Completed', displayFont);
  d.open('GET', ‘CALIBRI.TTF’);
  d.send();
} 

In the event handler function, we cannot use the HTTP response as a string. However, the <TextBlock> object supports a method called setFontSource(). If you provide the downloader object as an argument, you can use the font you just downloaded!

function displayFont(sender, eventArgs) {
  var textBlock = sender.findName('FancyText'),
  textBlock.setFontSource(sender); 

Remember that the downloader object is automatically the event sender and, therefore, the first argument for any event handler function attached to the object.

The rest is easy: you can now set the fontSize property of the <TextBlock> element to the name of the downloaded font:

  textBlock.fontFamily = ‘Calibri’;
} 

Example 16-7 contains the complete code, and you can see the result in Figure 16-4.

Example 16-7. Loading a font, the JavaScript file (TrueTypeFont.js)

function loadFont(sender, eventArgs) {
  var plugin = sender.getHost();
  var d = plugin.createObject('downloader'),
  d.addEventListener('Completed', displayFont);
  d.open('GET', 'CALIBRI.TTF'),
  d.send();
}

function displayFont(sender, eventArgs) {
  var textBlock = sender.findName('FancyText'),
  textBlock.setFontSource(sender);
  textBlock.fontFamily = 'Calibri';
} 
The text now uses the Calibri font, which is on all supported systems

Figure 16-4. The text now uses the Calibri font, which is on all supported systems

But that’s not all! You can even download a ZIP file containing several font files, and use every font in there. The XAML file does not change from that shown in Example 16-6, except for one thing: clicking on the canvas executes an event handler. The application will change the currently displayed font whenever the user clicks on it. See Example 16-8 for the code.

Example 16-8. Loading multiple fonts, the XAML file (TrueTypeFonts.xaml)

<Canvas xmlns="http://schemas.microsoft.com/client/2007" 
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Loaded="loadFont" MouseLeftButtonDown="displayFont">
  <Rectangle Width="300" Height="150" Stroke="Orange" StrokeThickness="15" />
  <TextBlock x:Name="FancyText" FontSize="64" Canvas.Left="20" Canvas.Top="35"
             Foreground="Black" Text="Silverlight" />
</Canvas> 

Now create a ZIP file containing several TTF files; we are using Consolas and Calibri here. Then load the ZIP file as usual, in the loadFont() function. The displayFont() function will now be called on two occasions: when the font ZIP file has been loaded and when the user clicks on the rectangle. In the former case, the font source of the text box needs to be set to the downloader object. This works only if the event sender (first argument of the event handler function) is the downloader object, and not the <Canvas> element (when the user clicks it). A try...catch block avoids any JavaScript errors:

function displayFont(sender, eventArgs) {
  var textBlock = sender.findName('FancyText'),
  try {
    textBlock.setFontSource(sender);
  } catch (ex) {
  }
  //...
} 

The rest is easy. Once the font source is set, you can set the TextBlock element’s fontFamily property to the name of any font within the ZIP file. Example 16-9 shows the complete code, which also sets the correct font size depending on the font being used. You can see in Figure 16-5 that the application now also uses the Consolas font.

Example 16-9. Loading multiple fonts, the XAML JavaScript file (TrueTypeFonts.xaml.js)

var font = 'Calibri';

function loadFont(sender, eventArgs) {
  var plugin = sender.getHost();
  var d = plugin.createObject('downloader'),
  d.addEventListener('Completed', displayFont);
  d.open('GET', 'fonts.zip'),
  d.send();
}

function displayFont(sender, eventArgs) {
  var textBlock = sender.findName('FancyText'),
  try {
    textBlock.setFontSource(sender);
  } catch (ex) {
  }
  if (font == 'Calibri') {
    font = 'Consolas';
    var fontSize = '40';
  } else {
    font = 'Calibri';
    var fontSize = '64';
  }
  textBlock.fontFamily = font;
  textBlock.fontSize = fontSize;
} 
Clicking on the canvas changes the font

Figure 16-5. Clicking on the canvas changes the font

So, you can use the downloader class not only to download text information, but also to provide dynamic resources such as TrueType/OpenType fonts.

Further Reading

Microsoft download center, http://www.microsoft.com/downloads/details.aspx?FamilyID=22e69ae4-7e40-4807-8a86-b3d36fab68d3&DisplayLang=en

Consolas font (for registered Visual Studio 2005 users)

..................Content has been hidden....................

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