10 Minute Tutorial - Silverlight: Using JavaScript to Call Scriptable Managed Code (C#)
My last Silverlight tutorial walked through manipulating a Silverlight control using managed code. So today, we will learn how to access that managed code from JavaScript in an HTML page. The blandness of my last tutorial (a blue rectangle, whoopie) inspired me to spice things up for this tutorial and give you: animated, multi-color rectangles! Blows your mind, doesn’t? Lacking the desire or the time to create the necessary XAML myself, I “borrowed” it from one of the samples embedded in the absolutely awesome SilverlightPad application.
Man, I hope that code is open source.
In addition to snazzy animations, the application for this tutorial will also contain a couple of form controls that will manipulate the Silverlight content at runtime. The first control, a simple drop down, will allow the user to select the number of rectangles displayed on the screen. The second form control will act as a pause/resume button for the animation.
Okay, let’s get started!
By the way, if this demo looks at all familiar, I “borrowed” the idea from the Bubblemark benchmarking application. I like to “borrow” stuff.
Prerequisites
- All the prerequisites defined in last Silverlight tutorial.
- This tutorial in particular builds on concepts discussed in all my previous Silverlight tutorials, so if you don’t understand something I suggest working backwards thru the tutorials until you can follow.
QuickStart
If you want to see the end result of this tutorial and you have installed all the prerequisites, then please download the ZIP file below, unzip it and open the scriptable.html file in your browser.
- Scriptable Sample (you may read this software’s license here)
You can see it in action, here
Lesson
Step 1: Create the HTML
As always, we start with the HTML for our Silverlight applicatoin. Create a file named scriptable.html and put this HTML in it:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head>
<title>Scriptable Sample</title>
<script type="text/javascript" src="Silverlight.js"></script>
<script type="text/javascript" src="createSilverlight.js"></script>
</head>
<body>
<div id="silverlightControlHost">
</div>
<script type="text/javascript">
// Find the div by id
var hostElement = document.getElementById("silverlightControlHost");
// Create the Silverlight control
createSilverlight(hostElement);
</script>
<form>
<select name=”numberOfRectangles”>
<option value=”1″>One</option>
<option value=”2″>Two</option>
<option value=”3″>Three</option>
<option value=”4″>Four</option>
<option value=”5″>Five</option>
<option value=”6″ selected=”selected”>Six</option>
</select>
<input type=”button” name=”pauseResumeButton” value=”Pause/Resume” />
</form>
</body>
</html>
Note that this HTML file breaks from tradition a bit. As I mentioned before, this time our HTML will contain a couple of form contorls, highlighted in red, that will send events to our Silverlight control. Both the drop down and button lack any JavaScript events for now, but we’ll add those later.
Step 2: Understand the XAML
Since this tutorial has a large amount of XAML, I won’t post it inline. You can download the XAML file for this tutorial here. Open this file up in your favorite text editor and look at lines 27-33:
<Rectangle x:Name="orangeRect" Opacity=".65" Fill="orange" Height="100" Width="100" RadiusX="10" RadiusY="10" /> <Rectangle x:Name="blueRect" Opacity=".65" Fill="blue" Height="100" Width="100" RadiusX="10" RadiusY="10" /> <Rectangle x:Name="redRect" Opacity=".65" Fill="red" Height="100" Width="100" RadiusX="10" RadiusY="10" /> <Rectangle x:Name="yellowRect" Opacity=".65" Fill="yellow" Height="100" Width="100" RadiusX="10" RadiusY="10" /> <Rectangle x:Name="greenRect" Opacity=".65" Fill="green" Height="100" Width="100" RadiusX="10" RadiusY="10" /> <Rectangle x:Name="grayRect" Opacity=".65" Height="100" Width="100" RadiusX="10" RadiusY="10">
As I mentioned in the introduction, this tutorial will contain animated rectangles and these lines of XAML declare six rectangles of various colors. The declaration of the animation starts at line number 11:
<Storyboard x:Name="theStoryboard" BeginTime="0" Duration="Forever">
As you could probably surmise from the name, the Storyboard object controls the animation. Even if you don’t know very much about Windows Presentation Foundation (WPF), if you look at one of the animation declarations inside this Storyboard object, it doesn’t take much brain power to figure out what happens:
<DoubleAnimation Storyboard.TargetName="orangeRect" Storyboard.TargetProperty="(Canvas.Top)" From="0" To="300" AutoReverse="true" BeginTime="0:0:0" Duration="0:0:2" RepeatBehavior="Forever"/> ...
So, this Storyboard object will update the value of the Canvas.Top property of the Rectangle named orangeRect starting at 0, going to 300, and then “auto-reversing” back to 0 forever.
So, the orange Rectangle will be suffering from a bit vertigo. Sweet.
- Learn more about the Storyboard element
- Learn more about animation with Windows Presentation Foundation
Step 3: Initialize the Silverlight control with JavaScript
Now, on to the all too familiar createSilverlight.js file:
function createSilverlight( controlHost )
{
Silverlight.createObjectEx({
source: "scriptable.xaml",
parentElement: controlHost,
id: "scriptableSilverlightControl",
properties: {
width: "500",
height: "500",
version: "0.95",
background: "white",
isWindowless: "true",
enableHtmlAccess: "true"
},
events: {}
});
}
So this time, our control will render a XAML file named scriptable.xaml. Not a big surprise.
Step 4: Create the C# code
Now comes the C#. First create a file named Scriptable.xaml.cs and declare a class called ScriptableCanvas:
namespace ScriptableApplication
{
using System;
using System.Windows;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Controls;
using System.Windows.Shapes;
using System.Windows.Browser;
public class ScriptableCanvas : Canvas
{
public ScriptableCanvas()
{
}
}
}
We’ll use an array to track the six floating rectangles defined in our XAML (orangeRect, blueRect, etc) and assign the array elements when the XAML finishes loading:
private Rectangle[] rects = new Rectangle[6];
public void ScriptableCanvas_Loaded(object o, EventArgs e)
{
rects[0] = this.FindName("orangeRect") as Rectangle;
rects[1] = this.FindName("blueRect") as Rectangle;
rects[2] = this.FindName("redRect") as Rectangle;
rects[3] = this.FindName("yellowRect") as Rectangle;
rects[4] = this.FindName("greenRect") as Rectangle;
rects[5] = this.FindName("grayRect") as Rectangle;
}
As mentioned in the introduction, for this tutorial, the user has the option of showing any number of rectangles they want, so let’s create a function for that:
public void ShowRectangles(int numberOfRectangles)
{
for( int rectangleIndex = 0; rectangleIndex < 6; rectangleIndex++ ) {
if( rectangleIndex < numberOfRectangles ) {
rects[rectangleIndex].Visibility = Visibility.Visible;
}
else {
rects[rectangleIndex].Visibility = Visibility.Collapsed;
}
}
}
The user can also pause and resume the animation at will. In this case, we have a WPF Storyboard object that controls our animation, so we need to get a reference to that in the ScriptableCanvas_Loaded function. We also need a helper variable to let us know the current state of the Storyboard:
private Storyboard theStoryboard = null; private bool isPaused = false; public void ScriptableCanvas_Loaded(object o, EventArgs e) { theStoryboard = this.FindName(”theStoryboard”) as Storyboard; rects[0] = this.FindName(”orangeRect”) as Rectangle; rects[1] = this.FindName(”blueRect”) as Rectangle; rects[2] = this.FindName(”redRect”) as Rectangle; rects[3] = this.FindName(”yellowRect”) as Rectangle; rects[4] = this.FindName(”greenRect”) as Rectangle; rects[5] = this.FindName(”grayRect”) as Rectangle; }
With access to the Storyboard, we can start and stop the animation with only a few lines of code:
public void PauseResume()
{
if( isPaused ) {
theStoryboard.Resume();
isPaused = false;
} else {
theStoryboard.Pause();
isPaused = true;
}
}
Step 5: Make it scriptable
Now, with all the functionality defined, let’s make it scriptable. We do this by using the Scriptable attribute on all the functions and classes we want available to the browser’s JavaScript engine. Below, I’ve posted the code for Scriptable.xaml.cs in its entirety:
namespace ScriptableApplication
{
using System;
using System.Windows;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Controls;
using System.Windows.Shapes;
using System.Windows.Browser;
[Scriptable]
public class ScriptableCanvas : Canvas
{
private Rectangle[] rects = new Rectangle[6];
private Storyboard theStoryboard = null;
private bool isPaused = false;
public ScriptableCanvas()
{
}
public void ScriptableCanvas_Loaded(object o, EventArgs e)
{
WebApplication.Current.RegisterScriptableObject( “ScriptableCanvas”, this );
theStoryboard = this.FindName(”theStoryboard”) as Storyboard;
rects[0] = this.FindName(”orangeRect”) as Rectangle;
rects[1] = this.FindName(”blueRect”) as Rectangle;
rects[2] = this.FindName(”redRect”) as Rectangle;
rects[3] = this.FindName(”yellowRect”) as Rectangle;
rects[4] = this.FindName(”greenRect”) as Rectangle;
rects[5] = this.FindName(”grayRect”) as Rectangle;
}
[Scriptable]
public void ShowRectangles(int numberOfRectangles)
{
for( int rectangleIndex = 0; rectangleIndex < 6; rectangleIndex++ ) {
if( rectangleIndex < numberOfRectangles ) {
rects[rectangleIndex].Visibility = Visibility.Visible;
}
else {
rects[rectangleIndex].Visibility = Visibility.Collapsed;
}
}
}
[Scriptable]
public void PauseResume()
{
if( theStoryboard.GetIsPaused() ) {
theStoryboard.Resume();
} else {
theStoryboard.Pause();
}
}
}
}
So, Silverlight will make the class ScriptableCanvas and its ShowRectangles and PauseResume functions available to the browser’s JavaScript engine. We will need to tell Silverlight what JavaScript variable we want to bind to the current instance of the ScriptableCanvas class. We do that by calling the WebApplication.Current.RegisterScriptableObject function passing the name of JavaScript variable we want to declare as the first parameter and the object it binds to as the second parameter.
Unfortunately, I couldn’t find much documentation on the RegisterScriptableObject or WebApplication object, but hopefully, Microsoft will provide more details on how these objects work as we get closer to the Silverlight 1.1 release.
Step 6: Compile it
The code for this application will reside in a DLL named “ScriptableCanvas”, so the command line needed to compile will look like this:
C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\csc.exe /t:library /nostdlib+ /noconfig /out:ScriptableCanvas.dll /lib:"C:\Program Files\Microsoft Silverlight" /r:agclr.dll;mscorlib.dll;system.dll;System.Core.dll;System.Silverlight.dll;System.Xml.Core.dll Scriptable.xaml.cs
Step 7: Connect the XAML to the C# code
Actually, I’ve already done this for you in the XAML file you downloaded in Step 2 (gee, aren’t I a sweetheart?). I just wanted to make sure you noticed the wiring of the Load event in the Canvas tag:
<Canvas xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" x:Class="ScriptableApplication.ScriptableCanvas;assembly=ScriptableCanvas.dll" Loaded="ScriptableCanvas_Loaded" Height="500" Width="500">
So, when this Canvas loads it will call the ScriptableCanvas_Loaded of our ScriptableCanvas object.
If you don’t undrestand anything I just wrote, please take a quick glance thru my previous tutorial.
Step 8: Wire the button click event
Now, with all the Silverlight code in place, let’s add those JavaScript events. First, the button:
<input type="button" name="pauseResumeButton" value="Pause/Resume" onclick=”document.getElementById(’scriptableSilverlightControl’).Content.ScriptableCanvas.PauseResume();” />
So, when the user clicks the pauseResumeButton, first the JS engine grabs the Silverlight control on the page using the document.getById function and the id of the Silverlight control that we passed into the Silverlight.createObjectEx function in our Silverlight.js file. Then, on that object, it gets the Content object which will contain all the objects registered in the call to WebApplication.Current.RegisterScriptableObject, in this case “ScriptableCanvas”. Finally, on Content.ScriptableCanvas it calls the PauseResume functon, made avaible to it by the [Scriptable] attribute.
Step 9: Wire the drop down change event
Now for the drop down:
<select name="numberOfRectangles" onchange=”document.getElementById(’scriptableSilverlightControl’).Content.ScriptableCanvas.ShowRectangles(Number(this.options[this.selectedIndex].value))”>
Here, JS accesses the same object heirarchy as in Step 8, and then call the ShowRectangles. The ShowRectangles function takes a single integer parameter (the number of rectangles to show), so we make sure to pass in the value from the selected list item.
Notice the use of type conversion. Before I added this type conversion, Silverlight would throw an exception because JavaScript passed in the parameter as a string. Converting it insures Silverlight gets a correctly typed parameter.
Step 10: Run it!
Go ahead and open the HTML page in your browser. You should see several rounded rectangles with nice gradients floating on your screen. I’ll let you figure out what the form controls do (it should be more than self-explanatory…we’re talking painfully obvious here
).
Conclusion
The ability to call managed code from JavaScript opens a whole new world of possibilities. Now, HTML and JavaScript savvy web designers who might not want to touch Visual Studio (or in our case, the command line compiler), can leverage your visually-appealing, managed code Silverlight controls and do all sorts of things with them you never imagined (legal, non-sexual things, of course…get your mind out of the gutter!
). I can see this laying a great foundation for a vibrant “Silverlight web component” community similar to the Flash and ActiveX component communities today. So, get out there and start coding!
Leave a Donation
If you found this article helpful, please leave a donation for Dave, so he can help you again. As always, thank you for your support!



