Silverlight and Flash Interoperability using HTML Bridge and ExternalInterface API: By Jonas Follesø 10

Note: This article is submitted by Jonas for Silverlight 2 Beta 2 – Articles Competition.Thanks a lot, Jonas! Hello All, Please drop a comment if you like it.

Contents

  • Introduction
  • Flash ExternalInterface API
  • Silverlight HTML Bridge
  • Calling Flash from Silverlight
  • Calling Silverlight from Flash
  • Silverlight web cam support using Flash interoperability
  • Summary

Download :SilverlightFlashIntegrationWeb.zip (1.43  MB)

Introduction

Silverlight and Flash have many similarities. Both are browser plug-ins that enables web developers to build richer internet applications. You can choose to have Silverlight or Flash occupy the entire screen and build all off the application, or you can have Silverlight or Flash embedded as small islands on your HTML page.
When you decide to include Silverlight or Flash on your page there are many scenarios where you may want to enable the application to interact with the rest of your page. You may want to read data from the HTML page, or update certain HTML elements at runtime. Perhaps you are gradually enhancing an existing web application and need to leverage existing JavaScript code running on the page. In these scenarios you need to leverage the browser integration model supported by Silverlight or Flash to communicate between your application and the hosting HTML page.

In this tutorial I’ll walk you through the basics of browser integration, and how you can make Silverlight and Flash talk to each. The tutorial starts off with some basic examples of browser integration in Flash and Silverlight, and move on to show how to enable webcam support in Silverlight using Flash.

Flash ExternalInterface API

Flash has been around for a long time and there have been different techniques to communicate between Flash and the. The “Flash JavaScript Integration Kit” is one example of such a framework. In Flash 8 Adobe included an official API to enable integration between your Flash application and the browser called the “ExternalInterface API”. The API consists of two main functions: the “addCallback” function to expose an ActionScript function to the web page, and the “call” function to invoke JavaScript functions on the web page.

The following code shows how to create a simple ActionScript function and expose it using the ExternalInterface API:


ExternalInterface.addCallback("saySomething", saySomething);

function saySomething():String
{
return "Hi, I'm Flash!";
}

To call the function from JavaScript you need to get a reference to the Flash player on the HTML page, and simply call the “saySomething” function:


function callFlash()
{
// Get a reference to the player
var player = null;

if (document.all)
player = document.all("flashPlayer");
else
player = document.getElementById("flashPlayerEmbedded");

alert(player.saySomething());
}

If you want to call a JavaScript function from ActionScript you simply use the “call” function on the ExternalInterface API:

function clicked(eventArgs:Object):void
{
ExternalInterface.call("saySomething", "What's up? I'm calling from Flash!");
}
this.btnSave.addEventListener("click", clicked);

Off course you also need to have the “saySomething” JavaScript function defined on your page:

function saySomething(something)
{
alert(something);
}

As you can see the ExternalInterface API makes it really easy to communicate between Flash and the browser. This first example use simple strings as parameters, but you can pass any ActionScript type across the ExternalInterace API. That being said, in many cases passing strings is all you need as you can format your data using JSON or XML.

Silverlight HTML Bridge

The browser integration layer in Silverlight 2 is often called the “HTML Bridge”, and enables you to interact with both HTML elements as well as JavaScript code running in the browser. This opens up for several interesting integration scenarios, some not even using Silverlight for the user interface at all.
At the core of the HTML bridge is the “ScriptObject”, which objects such as HtmlObject, HtmlPage, and HtmlElement derive from. To invoke the “saySomething” function from our previous example you simply write:


HtmlPage.Window.Invoke("saySomething", "Can you see the light? The Silverlight!");

The HTML bridge contains a lot of functionallity in addition to basic function invocation. It contains functionalliy to grab a HTML DOM Element and change it’s properties and style attributes. You can expose .NET types to the JavaScript runtime and then create new instances of the type from JavaScript. You can also expose instances of .NET objects to the JavaScript and directly interact with the object.

For a good introduction to interaction between HTML and managed code I recommend checking out the Silverlight quick starts at http://www.silverlight.net/QuickStarts/Dom/. The focus on this article will be on Silverlight 2 to Flash integration, and will not go in-depth on the Silverlight HTML Bridge.
Calling Flash from Silverlight

The Flash ExternalInterface API and the Silverlight HTML gives us the building blocks we need to start passing data between a Silverlight 2 and Flash application running on the same page. For this example we’re going to create a simple user interface to edit a person in both Flash and Silverlight. The interface looks something like this:

Calling Flash from Silverlight

The Flash ExternalInterface API and the Silverlight HTML gives us the building blocks we need to start passing data between a Silverlight 2 and Flash application running on the same page. For this example we’re going to create a simple user interface to edit a person in both Flash and Silverlight. The interface looks something like this:

In the example I want to show how to call Flash from Silverlight, and Silverlight from Flash. We’ll start by covering how to read a Person object from Flash into Silverlight when the user clicks the “Load from Flash” button.

The first thing we need to is create an ActionScript function to get a Person object from the Flash application. Both JavaScript and ActionScript are following based on the ECMAScript specification, and support object literals. This is a straight forward syntax to create new objects on the fly. For more information check out object literals in ECMAScript check out http://en.wikipedia.org/wiki/Object_literal.

ExternalInterface.addCallback("getPerson", getPerson);
function getPerson():Object
{
return { name: txtName.text, email: txtEmail.text, url: txtUrl.text };
}

The object returned contains three properties, name, email and url. To get the object from Flash into Silverlight we use a little JavaScript helper function:

function loadFromFlash()
{
var flash = getFlash();
if(flash)
{
return flash.getPerson();
}
}

The final piece is the C# code to handle the button click and call the JavaScript helper function. The code calls the JavaScript function and gets a generic ScriptObject back. This object contains a ConvertTo-method that tries to convert a script object to a .NET type. To hold the person data we’ve created a simple .NET person object. The property names have to match in order for the conversion to successes:

public class Person
{
public string name { get; set; }
public string url { get; set; }
public string email { get; set; }
}
private void btnLoad_Click(object sender, RoutedEventArgs e)
{
ScriptObject scriptObject =
(ScriptObject)HtmlPage.Window.Invoke("loadFromFlash");

var person = scriptObject.ConvertTo<Person>();
txtName.Text = person.name;
txtEmail.Text = person.email;
txtUrl.Text = person.url;
}

When we run the application we can enter data in the Flash application, and hit the “Load from Flash” button to update the Silverlight application. The next step is the “Save to Flash” functionality. When the user clicks the “Save to Flash” button we want to push the .NET object from Silverlight to Flash. The HTML bridge supports passing .NET objects from Silverlight to JavaScript by marking an object as scriptable:

[ScriptableType]
public class Person
{
[ScriptableMember]
public string name { get; set; }

[ScriptableMember]
public string url { get; set; }

[ScriptableMember]
public string email { get; set; }
}

Once the type is marked as scriptable we can pass it to any JavaScript function invoked using the HTML bridge. In theory we should be able to pass this type along to Flash, but for some reason the Flash plug-in is throwing an exception when you try passing it a scriptable type. There are several possible workarounds. One option is to pass the object to JavaScript, and then write JavaScript code to create a new object before passing on to Flash. Another option is to use the Silverlight JSON serializer and pass the object as a JSON-string to the JavaScript function, and then evaluate the string before passing it as a JavaScript object to Flash. I’ve included sample code for both solutions.

The first option is to pass the object directly to the JavaScript function, and then create a JavaScript object on the fly before passing it to Flash:

private void btnSave_Click(object sender, RoutedEventArgs e)
{
var person = new Person();
person.name = txtName.Text;
person.email = txtEmail.Text;
person.url = txtUrl.Text;
HtmlPage.Window.Invoke("saveToFlash", person);
}

The “saveToFlash” function creates a new JavaScript object before passing it along to Flash. The optimal solution would be to pass the object straight to Flash:

function saveToFlash(person)
{
var flash = getFlash();
if(flash)
{
var jsPerson = {
name: person.name,
email: person.email,
url: person.url
};
flash.setPerson(jsPerson);
}
}

The ActionScript function to update the Flash UI is really straight forward:

ExternalInterface.addCallback("setPerson", setPerson);

function setPerson(person:Object):void
{
txtName.text = person.name;
txtEmail.text = person.email;
txtUrl.text = person.url;
}

The second approach for passing our Person object from Silverlight to Flash is to serialize it to JSON before passing it to the browser. JSON stands for “JavaScript Object Notation”, and is a way to represent a full object graph as a string. Basically it’s just a string containing object literals, which can be evaluated by the JavaScript engine to build an object three. Silverlight has built-in support for JSON serialization, so implementing this is fairly straight forward. We’re going to use the DataContractJsonSerializer class, which is located in the System.ServiceModel.Web assembly under the System.Runtime.Serialization.Json namespace. We also need to reference the System.Runtime.Serialization assembly to get the DataContract and DataMember attributes we need to tag our Person object with.

After adding the DataContract and DataMember attributes our Person object now looks like this:

[DataContract]
public class Person
{
[DataMember]
public string name { get; set; }

[DataMember]
public string url { get; set; }

[DataMember]
public string email { get; set; }
}

Using C# 3.5 extension methods you attach a “ToJson”-method to all objects for an easy way to serialize any object. An extension method is basically a public static method in a static class, where the first parameter has the “this” keyword:

public static class Extensions
{
public static string ToJson(this object obj)
{
using(MemoryStream ms = new MemoryStream())
{
DataContractJsonSerializer serializer = new
DataContractJsonSerializer(obj.GetType());

serializer.WriteObject(ms, obj);
ms.Position = 0;
using(StreamReader reader = new StreamReader(ms))
{
return reader.ReadToEnd();
}
}
}
}

So in order to pass the JSON –serialized Person object to our JavaScript function we simply need to call the “ToJson” method on the person object:

private void btnSave_Click(object sender, RoutedEventArgs e)
{
var person = new Person();
person.name = txtName.Text;
person.email = txtEmail.Text;
person.url = txtUrl.Text;
HtmlPage.Window.Invoke("saveToFlash", person.ToJson());
}

We only need a minor change in the JavaScript function, where we evaluate the JSON string to build a JavaScript object we can pass to Flash:

function saveToFlash(person)
{
var flash = getFlash();
if(flash)
{
var jsPerson = eval('(' + person + ')');
flash.setPerson(jsPerson);
}
}

Calling Silverlight from Flash

So far we’ve covered how to call ActionScript functions from Flash. Now we want to do the opposite, call Silverlight functions from ActionScript. The first thing we want to implement is the “Load from Silverlight”-button. When the user clicks the button in Flash we want to call Silverlight to get our Person object. To do that we add a new method to your Silverlight application, as well as mark the user control as a scriptable type, and the method as a scriptable member:

[ScriptableType]
public partial class Page : UserControl
{
[ScriptableMember]
public string GetPerson()
{
var person = new Person();
person.name = txtName.Text;
person.email = txtEmail.Text;
person.url = txtUrl.Text;
return person.ToJson();
}
// Additonal code removed from example...
}

We also need to expose an instance of the user control to the browser so that we can call the GetPerson-method from JavaScript:

public Page()
{
InitializeComponent();
HtmlPage.RegisterScriptableObject("Page", this);
}

The next thing we need to do is to create a JavaScript function to be called from Flash to get the Person object from Silverlight:

function loadFromSilverlight()
{
var silverlight = getSilverlight();
if(silverlight)
{
var jsPerson = eval('(' + silverlight.content.Page.GetPerson() + ')');
return jsPerson;
}
}

To call the Silverlight method we get an instance of the Silverlight control and then access its content property. For each object we’ve registered as scriptable we can access it based on its key. The Page was registered with the key “Page”, so to syntax to call the GetPerson-method looks like this:

silverlight.content.Page.GetPerson()

The final step is to add the ActionScript code to handle the Flash button click event and call the JavaScript function:

function loadClicked(eventArgs:Object):void
{
var person = ExternalInterface.call("loadFromSilverlight");
txtName.text = person.name;
txtEmail.text = person.email;
txtUrl.text = person.url;
}
this.btnLoad.addEventListener("click", loadClicked);

Now that we can load from Silverlight to Flash we want to be able to save. The steps involved should be familiar by now. We start by adding a scriptable method on the Silverlight page:

[ScriptableMember]
public void SetPerson(ScriptObject scriptPerson)
{
var person = scriptPerson.ConvertTo<Person>();
txtName.Text = person.name;
txtEmail.Text = person.email;
txtUrl.Text = person.url;
}

The only important thing is the parameter type. We accept a ScriptObject which we convert to a Person object, before updating the UI. We also need to add some JavaScript to call from Flash:

function saveToSilverlight(person)
{
var silverlight = getSilverlight();
if(silverlight)
{
silverlight.content.Page.SetPerson(person);
}
}

In this example there is no type conversion, we’re passing the ActionScript person object straight to the SetPerson-method. We also need to add a click event handler for the Flash button:

function saveClicked(eventArgs:Object):void
{
ExternalInterface.call("saveToSilverlight",
{
name: txtName.text,
email: txtEmail.text,
url: txtUrl.text
});
}

Again we use object literals to create an on-the-fly person object that we pass to the JavaScript function.

Silverlight web cam support using Flash interoperability

Now that we’ve seen how we can integrate Silverlight and Flash using the HTML Bridge and ExternalInterface API it’s time to do something (somewhat) useful. We’re going to enable some basic webcam support in Silverlight using Flash interoperability.

Even though Flash and Silverlight have much of the same functionality, there are certain things you cannot do on both platforms. Local access of webcam and microphone is one example of something that is only possible in Flash. The way webcams work in Flash is through a camera–object. You attach the camera object to a video-object do display the video stream. The ActionScript code to create a camera and video looks like this:


var cam:Camera = Camera.getCamera();
cam.setMode(320, 240, 12);
var video:Video = new Video(320, 240);
video.attachCamera(cam);
this.addChild(video);

When you run your Flash application you’ll see a dialog asking if you want to give permission to access webcam and microphone. If you give the permission the video from your webcam should show on your screen.

With the video showing on screen, the next step is to add expose some ActionScript functions to take a snapshot of the video, and send the snapshot back to Silverlight. Flash supports drawing a frame from the video-object into a BitmapData-object. Using the open source as3corelib ActionScript extensions we can use a PNG-encoder to encode the BitmapData into a byte array. This byte array can be encoded as a Base64 string and passed to the browser using the ExternalInterface API:


import com.adobe.images.PNGEncoder;
import com.dynamicflash.util.Base64;

flash.system.Security.allowDomain("*");
ExternalInterface.addCallback("takeSnapshot", takeSnapshot);

function takeSnapshot():String
{
var bmd:BitmapData = new BitmapData(video.width, video.height,
false, 0x00CCCCCC);
bmd.draw(video, new Matrix());

var pngBytes:ByteArray = PNGEncoder.encode(bmd);
return Base64.encodeByteArray(pngBytes);
}

Using the code above we have a way to call our Flash application to get a Base64 encoded PNG image as a string. This string can be passed to Silverlight, which can decode the Base64 data into a byte array, and load the PNG image into a BitmapImage that can be used as the source of a Silverlight Image control. The JavaScript function to call Flash and pass the string back is really simple:

function takeSnapshot()
{
var flash = getFlash();
if(flash)
{
return flash.takeSnapshot();
}
}

The click event handler for the “take snapshot”-button calls the JavaScript function to get the Base64 encoded PNG image, decode it and set it as the source of a Silverlight image control:

void btnTakeSnapshot_Click(object sender, RoutedEventArgs e)
{
string base64Image = (string)HtmlPage.Window.Invoke("takeSnapshot");
if (base64Image != string.Empty)
{
byte[] imageData = Convert.FromBase64String(base64Image);
MemoryStream ms = new MemoryStream(imageData);
BitmapImage bitmapImage = new BitmapImage();
bitmapImage.SetSource(ms);
ms.Dispose();

snapshot.Source = bitmapImage;
}
}

Silverlight supports both JPG and PNG images, and the image returned from Flash is PNG encoded. All we have to do on our side is turn the Base64 string into a byte array, load it into a MemoryStream, and then load the MemoryStream into a BitmapImage. A BitmapImage can be used as the source for any Silverlight Image control.

Now that we got the PNG image in Silverlight we can do what ever we want with it. We can upload it to the server, we can use it in Silverlight animations, or perhaps for cassual games like a sliding photo puzzle.

Summary

Both Silverlight and Flash have APIs to bridge between the plug-in and the browser. Using this you can make Flash and Silverlight talk, using JavaScript as the bridge between the technologies. I hope this tutorial has given you a good introduction to both Silverlight and Flash browser integration, and how it can be used to bridge the technologies. The webcam example shows how we can use this technique to achieve something quite interesting. This is by no mean a substitute for a proper webcam API in some future Silverlight version, but it gives you some basic webcam support you can play with. I will definitely be posting more about webcam support in Silverlight, and some examples of what you can use it for.

All the code is included with this article, and is licensed under the MS-Pl license. The article is copyrighted under the creative commons license.

10 thoughts on “Silverlight and Flash Interoperability using HTML Bridge and ExternalInterface API: By Jonas Follesø

  1. Reply egoZd Jul 26,2008 7:08 pm

    This really a very great post.

  2. Pingback: Open Coding » Blog Archive » Won the First Prize!

  3. Reply unruledboy Jul 30,2008 12:10 am

    the solution is really good indeed, but I was wondering whether such situaion really exists in the real biz :)

  4. Reply kamal Sep 4,2008 7:33 am

    hi
    i need to read local system files using the flash object. then the content need to pass to a java script variable. please any help to me….

  5. Reply Victor Adolfsson Oct 21,2008 12:48 am

    Hi

    Thanks for a great post. Am I correct to assume that the takesnapshot always returns a 320*240 image even though the width and height of the video stream shown on the webpage is 640*480?.

    How do one go about to be able to get a 640×480 still image instead?
    best regards
    Victor

  6. Reply Carlos Nov 4,2008 7:14 am

    Thanks a lot for this post. :D

  7. Reply anon Feb 11,2009 4:42 pm

    Excellent! Thanks for the insightful info.

  8. Reply Duluth Garage Door installation Jan 10,2011 12:41 am

    I’d like to say for you to always provide clear information and image an avid reader of your site for quite a while. Just wanted to express gratitude really for all the favorable work you do!

  9. Reply Muhammad Kazim Oct 6,2011 2:50 am

    Hi

    I am facing an issue while running your sample project. Actually while i write some thing in flash application and press “load from flash” button, data not get, while “load from silverlight” button works properly. Waiting for reply.

  10. Reply Anwer Apr 3,2012 3:55 am

    Excellent work.

Leave a Reply