Silverlight, Firefox, and Inline Xaml: A Solution to the Problem

Silverlight is a new offering for developers that Microsoft is currently working on. Right now Silverlight 1.0 it is still in beta. Essentially, think Flash, except better. At least that’s the general idea. For more info, I would start with the Microsoft page for Silverlight. Chances are, though, that if you’re here, you know about it. But just in case, there you go.

There are currently two documented ways of loading Xaml for your Silverlight experience that I have seen. You can load from a file or you can have it inline in the page and load it from there. If your Xaml is static, put it in a separate file. That will probably make it easier for designers and a little separation is often helpful. Or, you can just inline it. If you want to get some data from Ajax or you want to build Xaml in response to user events, you can also load Xaml after the initial load and attach it to the existing control. If you want to have Xaml rendered on the server side when the page is rendered, render it as an inline Xaml block.

I have a little project that Silverlight will work great for, and as it turns out, it is the latter method that makes sense in the context of this application. Unfortunately, there is an issue. Silverlight, Firefox, inline Xaml, and a doctype of Xhtml 1.0 Transitional do not play well together. At all. According to a thread on the MSDN forums, this is actually caused by a documented bug in Firefox. This is, of course, annoying. If it was Silverlight’s bug they could just fix it by the next beta and we would be good to go.

The recommended solution in that forum post is to change or remove the xhtml doctype declaration from the page. Supposedly the problem is caused because Firefox parses the page incorrectly when the doctype is xhtml. Personally, I do not consider this to be a good solution. It works (I tested it), but I do not like it. This doctype issue is a biggie to me, and means I will probably not ever be inlining, especially since I figured out a workaround. Now I am sure that statement was not meant to be official "best practices" guidance. It is good advice for just getting it running. I just do not think it is a good practice to promote generally.

So how did I choose to solve this? Actually, it is not a terribly complicated or brilliant solution. But in the absence of some better/different "best practices" advice, this is how I will do it. I think it makes a lot of sense given two constraints. First, you either cannot change the doctype of the page or you do not want to. Second, it makes sense to build the Xaml during the normal execution of the page, not in a separate Ajax call. If you have all the data you need to generate the Xaml anyway when you are processing the page, letting that data go and retrieving it again via a second Ajax call is unnecessary resource expenditure. Given those two constraints, the following is how I decided to do it. You decide if it was the best way.

The method has the following logical steps:

  1. During the page execution, generate the Xaml you want to show in the control.
  2. Generate the javascript needed to load the Xaml, and assign insert the Xaml created in the previous step into the javascript output.
  3. When the page renders, load a blank canvas into the silverlight control.
  4. Have the javascript fire after the blank canvas has loaded, and append the new Xaml the Xaml tree of the blank canvas.

And with that, you are done. If you had to do that for every page you wanted an inline-ish Silverlight control, that would be...heinous. To solve this I packaged all that logic up into a server control, making it quite easy to do.

So this is what the aspx page would look like.



<%@ Page Language="C#" %>



<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">



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

<head runat="server">

    <title>Untitled Page</title>

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

</head>

<body>

<form id="form1" runat="server">

<div>

<p>Yay! All the silverlight code is contained in a control. You should see an obnoxious green square with the words 

"Add some custom Xaml!" below. But can you use the control multiple times on the same page? Let’s see if

<a href="ThisPageWorksFineMultipleInstances.aspx">the next example</a> works.</p>



<Silverlight:SilverlightInlineServerControl runat="server" ID="_silverlightControl"/>

</div>

</form>

</body>

</html>

And here is how an example rendered page would look. This html was taken from one of the pages in the download below.


<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">



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

<head><title>

Untitled Page

</title>

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

</head>

<body>

<form name="form1" method="post" action="ThisPageWorksFineReusable.aspx" id="form1">

<div>

<input type="hidden" name="__VIEWSTATE" id="__VIEWSTATE" value="/wEPDwUJNTA5MDMwNDk0ZGQb6TnJMpbdiIJnviY+XevxrkgzQQ==" />

</div>



<div>

<p>Yay! All the silverlight code is contained in a control. You should see an obnoxious green square with the words 

"Add some custom Xaml!" below. But can you use the control multiple times on the same page? Let’s see if 

<a href="ThisPageWorksFineMultipleInstances.aspx">the next example</a> works.</p>





<script type="text/javascript">

function _silverlightControlCanvasLoad(sender, eventArgs) 

{

    var control = sender.getHost();

    var xamlFragment = '<Canvas><TextBlock Canvas.Left="20" FontSize="24" Text="Add some custom Xaml!" /></Canvas>';

    if(xamlFragment != '')

    textBlock = control.content.createFromXaml(xamlFragment);

    sender.children.add(textBlock);

}

</script><div id="silverlightControlHost"></div>

<script type="text/javascript">Sys.Silverlight.createObject('CanvasHandler.ashx?id=_silverlightControl', document.getElementById('silverlightControlHost'), "_silverlightControlPluginName", { width:360, height:60, inplaceInstallPrompt:false, background:'#FF00CC00', isWindowless:'false', framerate:'24', version:'0.9' }, { onError:null, onLoad:null }, null);</script>

</div>

</form>



</body>

</html>



As you can see, the solution is not complicated. But is it the best way? I am not sure. When you need static Xaml, this method is not smart. When you need dynamic Xaml, then you may need to do something like this. You could load it separately via an Ajax javascript call, but sometimes this makes no sense. You could write a dynamically created Xaml file to disk, but then you have to deal with cleanup. Given the situation where you don’t want to change your doctype, and you don’t want to do a separate Ajax call, this is the best solution I have come up with so far. What do you think? If you have any recommendations, critiques, or whatever, feel free to make them here. If you want to download the above code, it is available below.

Notes About the Sample

  1. First, this is not intended to be the one Silverlight server control to rule them all. It is an example of a server-control-based approach to solving this. More tweaking needs to be done. It has its limitations and is meant simply to give you an idea how to solve this for yourself.
  2. This server control depends on an .ashx file in the web project. Well, that’s kinda brittle. But do note point #1. This thing is just a pointer.
  3. This control is made for inlining xaml. The control is not for loading Xaml by static file. See point #1.