MULTIMEDIA

Communicating with the Silverlight Plug-in

7/25/2010 5:07:40 PM
I_section1_d1e7073.html SilverlightSilverlightSilverlight

Once your JavaScript code has access to the plug-in, you can find details about the plug-in and its configuration as well as achieve relatively flexible access to the Silverlight content. This section features some scenarios and examples.

1. Determining Plug-in Settings

The first example will determine some of the plug-in's settings. It will also feature both access methods by using the JavaScript DOM and the JavaScript code in the XAML code-behind. First, we need the containing HTML page, where we load the Silverlight content. A button on the page will be used to trigger the retrieval and display of plug-in information. Example 1 shows the code; for the sake of legibility, we shortened the error handler (onError).

Example 1. Displaying plug-in information, the HTML file (Info.html)

<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Silverlight</title>

<script type="text/javascript" src="Silverlight.js"></script>
<script type="text/javascript" src="Info.js"></script>

<style type="text/css">
#errorLocation {
font-size: small;
color: Gray;
}
#silverlightControlHost {
height: 300px;
width: 400px;
}

</style>
</head>

<body>
<!-- Runtime errors from Silverlight will be displayed here.
This will contain debugging information and should be removed or hidden when
debugging is completed -->
<div id='errorLocation'></div>

<div id="silverlightPlugInHost">
<script type="text/javascript">
if (!window.Silverlight)
window.Silverlight = {};

Silverlight.createObjectEx({
source: 'Info.xaml',
parentElement: document.getElementById('silverlightPlugInHost'),
id: 'silverlightPlugIn',
properties: {
width: '100%',
height: '300px',
background:'white',
version: '1.0'
},
events: {
onError: null
},
context: null
});
</script>
</div>
<div>
<form action="">
<input type="button" value="Show plugin info" onclick="showInfoJS();" />
</form>
</div>
</body>
</html>

In the code-behind JavaScript file Info.js, the showInfoJS() function accesses the plug-in using the DOM, and then calls another function called showInfo(). This latter function will be implemented later on:

function showInfoJS() {
var plugin = document.getElementById('silverlightPlugIn');
showInfo(plugin);
}

Example 2 shows the XAML file without any complicated XAML markup, but an event handler for the left mouse button is attached to the canvas.

Example 2. Displaying plug-in information, the XAML file (Info.xaml)

<Canvas xmlns="http://schemas.microsoft.com/client/2007" 
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
MouseLeftButtonDown="showInfoXaml">
<Rectangle Width="300" Height="150" Stroke="Orange" StrokeThickness="15" />
<TextBlock FontFamily="Arial" FontSize="56" Canvas.Left="25" Canvas.Top="40"
Foreground="Black" Text="Silverlight" />
</Canvas>

The JavaScript file associated with the XAML file, displayed in Example 3, determines the plug-in and submits it as an argument to showInfo(), which is the same function that Info.html.js is using. However, from the browser's point of view, there is no difference between them whether JavaScript code is logically tied to the HTML page or to the XAML data. Therefore, we put both code snippets in one JavaScript file.

Example 3. Displaying plug-in information, the XAML JavaScript file (Info.js; excerpt)

function showInfoXaml(sender, eventArgs) {
var plugin = sender.getHost();
showInfo(plugin);
}

Finally, the showInfo() function needs to be implemented. It takes the plug-in reference, accesses two information points, and outputs them using the JavaScript alert() function. Example 4 shows the code. When you click on either the Silverlight content or the HTML button, you will get output similar to that shown in Figure 1.

Example 4. Displaying plug-in information, the HTML JavaScript file (Info.js; excerpt)

function showInfoJS() {
var plugin = document.getElementById('SilverlightPlugIn');
showInfo(plugin);
}

function showInfo(plugin) {
var s = 'Background: ' + plugin.settings.background;
s += '\nMaxFrameRate: ' + plugin.settings.maxFrameRate;
alert(s);
}

Figure 1. Displaying plug-in information


2. Modifying XAML Content

The plug-in's source property (plugin.content.source, to be exact) not only retrieves the XAML markup of the currently loaded Silverlight content, but also sets this information. This allows us to create a sample application with a similar concept as the WPF content viewer XAMLPad. An HTML text box enables users to enter XAML markup; JavaScript code then tries to load this markup into the Silverlight plug-in. Let's start with the HTML and the input field, as shown in Example 5.

Example 5. SilverlightPad, the HTML file (SilverlightPad.html)

<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Silverlight</title>

<script type="text/javascript" src="Silverlight.js"></script>
<script type="text/javascript" src="SilverlightPad.js"></script>

<style type="text/css">
#errorLocation {
font-size: small;
color: Gray;
}
#silverlightControlHost {
height: 300px;
width: 400px;
}

</style>
</head>

<body>
<!-- Runtime errors from Silverlight will be displayed here.
This will contain debugging information and should be removed or hidden when
debugging is completed -->
<div id='errorLocation'></div>

<div id="silverlightPlugInHost">
<script type="text/javascript">
if (!window.Silverlight)
window.Silverlight = {};

Silverlight.createObjectEx({
source: 'SilverlightPad.xaml',
parentElement: document.getElementById('silverlightPlugInHost'),
id: 'silverlightPlugIn',
properties: {
width: '100%',
height: '300px',
background:'white',
version: '1.0'
},
events: {
onError: null
},
context: null
});
</script>
</div>
<div id="InputHost">
<textarea id="XamlMarkup" style="width:600px;" rows="10"></textarea><br />
<input type="button" value="Update!" onclick="update();" />
</div>

</body>
</html>

The initial XAML markup is quite simple. In this example, it is the user's job to provide some fancy Silverlight content, after all. Example 6 shows the code.

Example 6. SilverlightPad, the XAML markup (SilverlightPad.xaml)

<Canvas xmlns="http://schemas.microsoft.com/client/2007" 
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Rectangle Width="600" Height="200" Stroke="Orange" StrokeThickness="15" />
<Canvas Canvas.Left="30" Canvas.Top="30" x:Name="XamlOutput">
<TextBlock FontFamily="Arial" FontSize="32" Foreground="Black"
Text="Enter data below ..."/>
</Canvas>
</Canvas>

The most sophisticated part of this example is certainly the JavaScript code. However, once you know how to do it, you just need a couple of lines. First, convert the XAML string provided by the user into a XAML JavaScript object that can work with the Silverlight plug-in. The plug-in.content.createFromXaml() method does exactly that:

var plugin = document.getElementById('silverlightPlugIn');
var obj = plugin.content.createFromXaml(
document.getElementById('XamlMarkup').value);

If the XAML is not valid, obj is null, so we can act accordingly. If, on the other hand, obj is not null, the user-supplied XAML is valid and can be applied to the page.

If you have a look back at Example 10-6, you will see that there is a second <Canvas> element within the root <Canvas>. The idea is that the outer (orange) border will always be there, whereas the content of that rectangle may be changed by the user. Therefore, the JavaScript code first needs to access the inner <Canvas>. You can do this by using findName():

var XamlOutput = plugin.content.findName('XamlOutput');

XamlOutput—and any other XAML JavaScript object—does not support the HTML DOM, but a similar API accesses all child elements: XamlOutput.children. The following properties and methods exist:


add()

Adds a child to the end of the list


clear()

Empties the children list


count

Provides the number of children


getItem()

Retrieves the child with a given index


getValue()

Retrieves the value of a given property


insert()

Inserts a child (second argument) at a given index (first argument)


name

Displays the name of the child (if available)


remove()

Removes a given child from the list


removeAt()

Removes a child with a given index


setValue()

Sets the value of a given property

For this example, we need to do only two things: delete all children of the inner <Canvas> element (imagine that a user has already submitted XAML that has been rendered in the plug-in) and add the XAML JavaScript object (obj) to the children list:
 if (obj != null) {
XamlOutput.children.clear();
XamlOutput.children.add(obj);
} else {
alert('Error! XAML might not be valid.');
}

Example 7 shows the complete JavaScript code (minus the createSilverlight() function), and Figure 10-2 presents SilverlightPad in action. The XAML entered in the text field is displayed within the orange border.

Example 10-7. SilverlightPad, the JavaScript file (SilverlightPad.js)

function update() {
var plugin = document.getElementById('silverlightPlugIn');
with (plugin.content) {
var XamlOutput = findName('XamlOutput');
var obj = createFromXaml(
document.getElementById('XamlMarkup').value);
}
if (obj != null) {
XamlOutput.children.clear();
XamlOutput.children.add(obj);
} else {
alert('Error! XAML might not be valid.');
}
}

Figure 10-2. SilverlightPad


NOTE

There is a simpler implementation. Instead of using the pseudo DOM of Silverlight, you can just set the source property of the plug-in.

3. Dumping Content Information

As a final sample application for this article, we will create a development helper tool that dumps information about a given Silverlight application. The sample features dynamic loading of Silverlight content and also recursive access of all elements. As usual, we start with the HTML file (see Example 8).

But there is a surprise—there is no createSilverlight() call, only the empty

container SilverlightPlugInHost. Additionally, there are three more HTML UI widgets on the page:

  • A text field (XamlFile) where users may enter the path to a XAML file.

  • A button that triggers loading the file.

  • A button that triggers dumping the file's contents. This button is initially disabled (why dump data when no data has been loaded yet?).

The page also contains an empty

element, called OutputDiv, where the "data dump" will be later displayed.

Example 8. Dumping Silverlight content, the HTML file (SilverlightDumper.html)

<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Silverlight</title>

<script type="text/javascript" src="Silverlight.js"></script>
<script type="text/javascript" src="SilverlightDumper.js"></script>

<style type="text/css">
#errorLocation {
font-size: small;
color: Gray;
}
#silverlightControlHost {
height: 300px;
width: 400px;
}

</style>
</head>

<body>
<form>
<div>
<div id="silverlightPlugInHost">
</div>

<div id="LoadXamlFile">
<input type="text" name="XamlFile" />
<input type="button" value="Load file" onclick="loadFile(this.form);" />
&mdash;
<input type="button" id="DumpButton" value="Dump XAML" onclick="dump();"
disabled="disabled" />

</div>
<div id="OutputDiv">
</div>

</div>
</form>
</body>
</html>

In the JavaScript code-behind file, the loadFile() function is responsible for loading the XAML file the user previously selected. First, the filename needs to be determined:

function loadFile(f) {
var filename = f.elements['XamlFile'].value;
...
}

NOTE

Note that in Example 8, the text field doesn't have an id attribute, just a name attribute. So, you can't use document.getElementById() to access the form element. However, you can use the JavaScript document.forms[0].elements array to read out the name. In this special case, we saved some typing. When we called loadFile(this.form) from the HTML page, a reference to the current form was submitted to loadFile(). Therefore, f.elements['XamlFile'] grants access to the form field, and the value property contains the field's contents.

This filename is then provided to a function named createSilverlight(). This function actually is a modified implementation of the JavaScript code that usually loads the Silverlight content. The modified version accepts the XAML filename to load as a parameter, allowing the application to dynamically load XAML files. The loadFile() function also enables the button that takes care of the (not yet implemented) dumping of Silverlight contents:

function loadFile(f) {
createSilverlight(f.elements['XamlFile'].value);
document.getElementById('DumpButton').disabled = false;
}

Figure 3 shows that the code so far already works. Enter a (relative) file path to a XAML file, and click the first button. The XAML content is displayed above the form.

Figure 3. Dynamically loading a XAML file


The next step in our application is also the final one: dumping the contents of the currently loaded XAML file. To implement this, the code first needs to access the plug-in, as always:

var plugin = document.getElementById('silverlightPlugIn');

We want to dump all elements, so we have to start at the top. The root property provides us with a reference to the root (<Canvas>) node:

var rootNode = plugin.content.root;

All that's left to do is to recursively dump the contents starting with the root node, and then display this information in the output <div>:

var html = dumpNode(rootNode);
document.getElementById('OutputDiv').innerHTML = html;

Admittedly, that's cheating, since we did not implement the dumpNode() function yet. But doing so is not that hard: dumpNode() expects a node as its first argument. The function then notes the string representation of the node (e.g., <Canvas> has the string representation Canvas). If the node has a name, the name is remembered as well:

function dumpNode(node) {
var html = node.toString();
if (node.name) {
html += ' (' + node.name + ')';
}

Now comes the tricky part. What if the node has child nodes? In that case, recursion comes into play: for every child node, dumpNode() calls dumpNode() again so that every child node is dumped as well. To visualize the hierarchy of the document, all child nodes are indented, using the <blockquote> HTML element.

There is one catch, however: if a node doesn't have children, accessing node.chidren will result in a JavaScript error. So, we need to use a try...catch statement instead:

  try {
if (node.children.count > 1) {
html += '<blockquote>';
for (var i = 0; i < node.children.count; i++) {
html += dumpNode(node.children.getItem(i));
}
html += '</blockquote>';
}
} catch (ex) {
}

Finally, the dumpNode() function adds a line break at the end and returns the HTML markup:

  html += '<br />';
return html;
}

NOTE

For security reasons, you may want to HTML-escape the element names by replacing &, <, >, ', and " with &amp;, &lt;, &gt;, &apos;, and &quot; (in that specific order!).

Refer to Example 9 for the complete JavaScript code. Figure 4 shows the data dumped for a simple "Hello World" file.

Example 9. Dumping Silverlight content, the JavaScript file (SilverlightDumper.html.js)

function createSilverlight(XamlFile)
{
Silverlight.createObjectEx({
source: XamlFile,
parentElement: document.getElementById('silverlightPlugInHost'),
id: 'silverlightPlugIn',
properties: {
width: '600',
height: '300',
background:'#ffffffff',
isWindowless: 'false',
version: '1.0'
},
events: {
onError: null
}
});
}

function loadFile(f) {
createSilverlight(f.elements['XamlFile'].value);
document.getElementById('DumpButton').disabled = false;
}

function dump() {
var plugin = document.getElementById('silverlightPlugIn');
var rootNode = plugin.content.root;
var html = dumpNode(rootNode);
document.getElementById('OutputDiv').innerHTML = html;
}

function dumpNode(node) {
var html = node.toString();
if (node.name) {
html += ' (' + node.name + ')';
}
try {
if (node.children.count > 1) {
html += '
';
for (var i = 0; i < node.children.count; i++) {
html += dumpNode(node.children.getItem(i));
}
html += '
';
}
} catch (ex) {
}
html += '
';
return html;
}


Figure 4. Dumping Silverlight data


Combining HTML, JavaScript, and Silverlight makes sense in many scenarios. For instance, remember that Silverlight does not offer a text input control yet, but HTML does. So, you could use HTML to enter data, and Silverlight to process it.

Other  
 
Most View
Apricorn Velocity Solo X2 PCle SSD Adapter
Tablet For Home – November 2012
Windows 7 : Interacting with the Built-In Security Features - WORKING WITH AUTOMATIC UPDATES (part 1)
Three Quick Ways To View Notification Center
Canon Powershot N 12.1MP Digital Camera with Wi-Fi (Part 1)
ASRock Z77 Extreme3 LGA 1155 Mainboard - Simple, Reasonable And Extraordinary (Part 2)
Mirrorless Cameras Supporting Good Filming
Migrating to Windows Small Business Server 2011 : Performing Post-Migration Tasks (part 3)
Sharepoint 2010 : Using InfoPath 2010 to Create Electronic Forms (part 1) - Creating an InfoPath Form
Sony Alpha 37 - Class-Leading Performance (Part 2)
Top 10
Lenovo Idea Pad Flex 14
New Smartphone Range From Kazam
Nikon D3500 Digital SLR Camera (Part 2)
Nikon D5300 Digital SLR Camera (Part 1)
The Big Projector - BenQ W770ST
The Best Motherboards For $138 Or Less (Part 3) - LGA 1155 Motherboards
The Best Motherboards For $138 Or Less (Part 2) - LGA 1150 Motherboards
The Best Motherboards For $138 Or Less (Part 1)
The Excellent EVGA Card - PNY GTX 780 Ti XlR8
Lap Test – Asus Z87-Deluxe