10 Minute Tutorial - Silverlight: Using JavaScript to Call Scriptable Managed Code (C#)

My event handling tutorial walked through manipulating a Silverlight control using managed code. 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! 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!

Prerequisites

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.

You can see it in action, here

Lesson

Step 1: Create the HTML

As always, we start with the HTML. 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" onchange="document.getElementById('scriptableSilverlightControl').Content.scriptableControl.ShowRectangles(Number(this.options[this.selectedIndex].value))">
        <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" onclick="document.getElementById('scriptableSilverlightControl').Content.scriptableControl.PauseResume();" />
    </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 animation declaration 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.

Step 3: Initialize the Silverlight control with JavaScript

Now, on to the all too familiar createSilverlight.js file:


function createSilverlight( controlHost )
{
  Silverlight.createObjectEx({
    source: "ClientBin/ScriptableApplication.xap",
    parentElement: controlHost,
    id: "scriptableSilverlightControl",
    properties: {
      width: "500",
      height: "500",
      version: "2.0.31005.0",
      background: "white",
      isWindowless: "true",
      enableHtmlAccess: "true"
    },
    events: {}
  });
}

So this time, our control will start the application embedded in ScriptableApplication.xap. Not a big surprise.

Step 4: Create the C# code

Now comes the C#. First create a file named ScriptableControl.xaml.cs and declare a class called ScriptableControl:


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 partial class ScriptableControl : UserControl
  {
    private Rectangle[] rects = new Rectangle[6];
    private bool isPaused = false;
 
    public ScriptableControl()
    {
              InitializeComponent();
    }      
  }
}

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 ScriptableControl_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 up to six rectangles, so let’s create a function for that:


public void ShowRectangles(int numberOfRectangles)
{
  for( int rectangleIndex = 0; rectangleIndex &lt; 6; rectangleIndex++ ) {
    if( rectangleIndex &lt; numberOfRectangles ) {
      rects[rectangleIndex].Visibility = Visibility.Visible;
    }
    else {
      rects[rectangleIndex].Visibility = Visibility.Collapsed;
    }
  }
}

The user can also pause and resume the animation at will. As noted in Step 2, we have a WPF Storyboard object that controls our animation, so we need to get a reference to that object. We’ve been using the “FindName” function for this, but since XAML compilation automatically creates a variable for us named “theStoryboard” (the value of the “x:Name” attribute, of the Storyboard element), we can just use this variable directly. Obviously, directly referencing the variable provides faster access than using the “FindName” function, but I wanted to demonstrate both methods.

With access to the Storyboard, we can start and stop the animation using only a few lines of code:


private bool isPaused = false;      
 
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 ScriptableMember attribute on all the functions and classes we want available to the browser’s JavaScript engine. Below, I’ve posted the code for ScriptableControl.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;
 
  public partial class ScriptableControl : UserControl
  {
    private Rectangle[] rects = new Rectangle[6];
    private bool isPaused = false;
 
    public ScriptableControl()
    {
              InitializeComponent();
    }      
 
    public void ScriptableControl_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;
    }
 
    [ScriptableMember()]
    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;
        }
      }
    }
 
    [ScriptableMember()]
    public void PauseResume()
    {
      if( isPaused ) {
        theStoryboard.Resume();
        isPaused = false;
      } else {
        theStoryboard.Pause();
        isPaused = true;
      }
    }
  }
}

The “ScriptableMember” attribute makes the functions available to Javascript. Now, let’s wire our new class to the XAML.

Step 6: 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 UserControl tag:


<UserControl xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    x:Class="ScriptableApplication.ScriptableControl"
    Loaded="ScriptableControl_Loaded">

So, when this UserControl loads it will call the ScriptableControl_Loaded function on our ScriptableControl object.

Step 8: Create the Application class

The App.xaml, App.xaml.cs and App.xml files for this application don’t differ much from the versions in my other tutorials (for example, here), so I will let you pull them from the sample code. The only major difference resides in the Application_Startup function in App.xaml.cs:


void Application_Startup(object sender, StartupEventArgs e)
{
  ScriptableControl scriptableControl = new ScriptableControl();
 
  this.RootVisual = scriptableControl;
 
  HtmlPage.RegisterScriptableObject("scriptableControl", scriptableControl );
}

The highlighted line of code registers the ScriptableControl object with the browser so Javascript can access it. Now we can call our C# functions from Javascript.

Step 9: Wire the button click event

Let’s add those JavaScript calls. First, the button:


<input type="button" name="pauseResumeButton" value="Pause/Resume" onclick="document.getElementById('scriptableSilverlightControl').Content.scriptableControl.PauseResume();" />

So, when the user clicks the pauseResumeButton, first the Javasript 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 HtmlPage.RegisterScriptableObject, in this case “scriptableControl”. Finally, on Content. scriptableControl it calls the PauseResume functon, made avaible to it by the “ScriptableMember” attribute.

Step 9: Wire the drop down change event

Now for the drop down:


<select name="numberOfRectangles" onchange="document.getElementById('scriptableSilverlightControl').Content.scriptableControl.ShowRectangles(Number(this.options[this.selectedIndex].value))">

Here, Javascript accesses the same object heirarchy as in Step 8, and then calls ShowRectangles. The ShowRectangles function takes a single integer parameter (the number of rectangles to show), so we 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: Build it

The sample code has a build file named “ScriptableApplication.csproj”. In comparison to the other tutorials, nothing special is going on here, so you can just copy it directly. Build it with this command, run from the command-line;


"C:\WINDOWS\Microsoft.NET\Framework\v3.5\msbuild.exe" ScriptableApplication.csproj
Step 11: Run it!

Go ahead and open scriptable.html in your browser. You should see several rounded rectangles 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, MSBuild), 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!

Share and Enjoy:
These icons link to social bookmarking sites where readers can share and discover new web pages.
  • Digg
  • StumbleUpon
  • Reddit
  • del.icio.us
Related Posts:
10 Most Important Points From Dr. Tim Sneath’s and Scott Guthrie’s Silverlight videos on Channel 9
Silverlight Portal
10 Things I Learned From the September MSDN Event in Atlanta
10 Minute Tutorial - Silverlight: Event handling using Managed Code (C#)

Comments

Comments are closed.