I’ve been working crazy hours updating my Silverlight course for version 2 and expanding it with lots of new material. With the PDC coming up in less than a week, I’ve also been working on some cool tips and tricks demonstrating some of the lesser-known but potentially useful features of Silverlight 2. Each day leading up to the show I’m going to try to post one juicy tip or trick. Here’s the first one.

Silverlight’s System.Windows.Browser namespace contains “HTML bridge” classes allowing managed code to integrate with the browser DOM. Among other things, you can use these classes to grab managed references to HTML DOM elements; call JavaScript functions from C#; call C# methods from JavaScript; process DOM events in C#; and handle events fired from C# with JavaScript.

Whenever you call from one language to another, one issue that’s sure to rear its head is how dataparticularly data types that don’t have an equivalent on the other sideis marshaled. The marshaling rules in Silverlight are complex but powerful. For example, when you call from C# into JavaScript, you can pass instances of managed types like this:

// Define the type

[ScriptableType]

public class Person

{

    public string Name { get; set; }

    public int Age { get; set; }

}

 

// Create a Person instance and pass it to JavaScript

Person person = new Person { Name = “Adam”, Age = 18 };

HtmlPage.Window.Invoke(“js_func”, person);

Silverlight’s marshaling layer uses reflection on the C# side to discover the particulars of the Person class, and then it creates a look-alike type on the JavaScript side. In JavaScript, you can consume a Person object like this:

function js_func(arg)

{

    var name = arg.Name;

    var age = arg.Age;

}

So far, so good. Passing instances of custom types from C# to JavaScript couldn’t be easier. The marshaling layer even honors by-ref and by-val semantics, so class instances are passed by reference and value types are passed by value.

Now suppose you want to pass a Person object from JavaScript to C#. You might do this on the JavaScript side, assuming the method is named CSharp_Method and it’s exposed by a scriptable type instance named “page”:

var person = new Object();

person.Name = ‘Adam’;

person.Age = 18;

var control = document.getElementById(‘SilverlightControl’);

control.content.page.CSharp_Method(person);

 

But in C#, the object comes through as a generic ScriptObject, which forces you to write code like this:

 

public void CSharp_Method(ScriptObject arg)

{

    string name = arg.GetProperty(“Name”).ToString();

    int age = Int32.Parse(arg.GetProperty(“Age”).ToString());

}

 

This is where HtmlPage’s rather obscure RegisterCreateableType method comes in handy. In C#, use RegisterCreateableType to turn Person into a class that can be instantiated in client-side script:

 

HtmlPage.RegisterCreateableType(“Person”, typeof(Person));

 

Then, in JavaScript, create a Person instance and pass it to C# like this:

 

var control = document.getElementById(‘SilverlightControl’);

var person = control.content.services.createObject(‘Person’);

person.Name = ‘Adam’;

person.Age = 18;

control.content.page.CSharp_Method(person);

 

The C# method can now be written as follows:

 

public void CSharp_Method (Person person)

{

    string name = person.Name;

    int age = person.Age;

}

 

Now the Person object really is a Person object, and you can consume it with all the benefits of strong typing.

Does that mean you won’t be needing the ScriptObject class any more? Hardly. Stay tuned for cool Silverlight trick #2, which uses ScriptObject to allow two Silverlight control instances to communicate without involving JavaScript.