Silverlight and the HTML page: let your Silverlight do the work

One of the things I like with having Silverlight 2 runtime on the browser is that it gives us the power of .NET at the browser level. And this is more than just for creating RIAs, one aspect that does not get much attention is the fact that you can use the Silverlight plugin to have extra power at the browser level. With this I mean taking advantage of all the power of the .NET framework and your ease of work with C# to get things done in the browser.

Even with the power of JavaScript debugging in Visual Studio 2008, if like me you are not a fan of lengthy applications in JavaScript this is a new possibility: just let Silverlight work with web services and processing and integrate that with your HTML page (DOM).

You can use the Silverlight plugin without even visually showing it, if that would be a requirement. In this post I will show a few of the possibilities that you can use in your Silverlight classes to make them available to the hosting HTML page and even manipulate your page’s DOM from the Silverlight application.

For this post I’m making a small demo application where an HTML page shows a list of thumbnails from Flickr photos together with the name. The call to Flickr is done by accessing Flickr’s REST API. What I’m doing is actually passing the calls that I would normally do using Ajax into the Silverlight application. As we all know it requires some work to get our Ajax working correctly in all browsers, with this approach we let Silverlight do the processing.

Tools used: Visual Studio 2008 with the Silverlight 2 Beta 1 Tools for VS.

Creating the Silverlight project

Create a Silverlight Application project in Visual Studio. In my case I've named the application "FlickrPhotosSL". I've chosen to automatically create the web application as well as I'll be using the web application to create the HTML page in which to show the pictures. Everything is added to the default.aspx page that was created by Visual Studio in the web application. Please note that I'm using ASPX here but could as well be using a static HTML page. The advantage of using an ASP.NET is we can take advantage of the asp:Silverlight control.

Accessing the DOM in Silverlight

Because I don't want to display anything in the Silverlight control I'll just change the background to the color of the HTML page.
Note: I did not find a way to completely hide the Silverlight plugin, will have to check how there is a better way to do this than setting a similar background. I tried setting the CSS class visibility to hidden but this causes the Silverlight plugin not to load anymore.

 <div id="test"></div>
<div>
      <asp:Silverlight ID="Silverlight1" runat="server" 
        Source="~/ClientBin/FlickrPhotosSL.xap" 
        Version="2.0" Width="10" Height="10" />
</div>

Let's start with a small test to get a DIV from the Silverlight application and change its innerHTML. To do this the DIV is added to the HTML page, together with the Silverlight object:

 <UserControl x:Class="FlickrPhotosSL.Page"
    xmlns="https://schemas.microsoft.com/client/2007" 
    xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml" 
    Width="0" Height="0">
    <Grid x:Name="LayoutRoot" Background="Transparent">
    </Grid>
</UserControl>

Then, in the Page.xaml.cs file we get a reference to the "test" DIV by calling HtmlPage.Document.GetElementById (lines 9 and 10).

    1:  public Page()
    2:  {
    3:          InitializeComponent();
    4:          this.Loaded += new RoutedEventHandler(Page_Loaded);
    5:   }
    6:   
    7:  void Page_Loaded(object sender, RoutedEventArgs e)
    8:  {
    9:          HtmlElement testDiv = HtmlPage.Document.GetElementById("test");
   10:          testDiv.SetAttribute("innerHTML", "Silverlight Page loaded");
   11:  }

The result is the text being displayed when running the page. Nothing fancy here but that works.

image

Now we'll remove the code used for the test in the HTML page and Page.xaml.cs. Next we add a textbox in plain HTML together with a button. This will be our textbox to type in the Flickr tag name to search for. The objective is to have the onclick event of the button to fire code in the Silverlight application by calling a method made available to the browser.

I'm also creating two DIVs: one for showing a list with pictures and another to be shown when the pictures are still loading.

image

Accessing Silverlight code from script

In the Silverlight code we we need to make some changes to the Page class so the object and methods are available to the script. .NET objects can be made available by using HtmlPage.RegisterScriptableObject() . By default the members of the object are not available in the browser, so we need to add the attribute [ScriptableMember].

So, we call RegisterScriptableObject in the page loaded and add a new method that will be called from script.

    1:  void Page_Loaded(object sender, RoutedEventArgs e)
    2:   {   
    3:         HtmlPage.RegisterScriptableObject("SilverlightFlickrObject", this);
    4:   
    5:   }
    6:   
    7:  [ScriptableMember]
    8:  public void SearchPictures(string tag)
    9:   {
   10:       ///code here
   11:   }

In order to call the SearchPictures method from script we create a reference to the Silverlight plugin and call the method in JavaScript. The method is available because it has been decorated by the ScriptableMember attribute. The loadPicturesSilverlight function is called by the onclick event of the button.

 <script type="text/javascript">
     function loadPicturesSilverlight()
     {
         var slPlugin = document.getElementById("Xaml1"); //Silverlight plugin
         var searchTag = document.getElementById("textTag"); //textbox
           
         slPlugin.content.SilverlightFlickrObject.SearchPictures(searchTag.value);
     }    
</script>
Calling the Flickr REST service in Silverlight

Now we only need to add code in Silverlight in order to search photos on Flickr and add them to the HTML page by generating HTML through Silverlight.

Because we are accessing the Flickr REST API we'll use WebClient to make an asynchronous call to the Flickr service. This will return us an XML document that we can parse and read to print out the picture thumbnails in the HTML page. You'll want to download the whole sample to view the complete as I'm only showing the main parts hereunder.

    1:  [ScriptableMember]
    2:   public void SearchPictures(string tag)
    3:  {
    4:         WebClient rest = new WebClient();
    5:         rest.DownloadStringCompleted += new DownloadStringCompletedEventHandler
 (rest_DownloadStringCompleted);
    6:        rest.DownloadStringAsync(new Uri(string.Format(flickrApi, tag)));
    7:   }
    8:   
    9:   void rest_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
   10:   {
   11:        string url = string.Empty;
   12:        string imageUrl = string.Empty;
   13:              
   14:        XDocument doc = XDocument.Parse(e.Result);
   15:   
   16:        HtmlElement ulList = HtmlPage.Document.CreateElement("ul");
   17:        picturesDiv.AppendChild(ulList); //pictures DIV
   18:   
   19:        picturesDiv.RemoveStyleAttribute("display");
   20:        picturesLoadingDiv.SetStyleAttribute("display", "block");
   21:   
   22:        picturesLoadingDiv.RemoveStyleAttribute("display");
   23:        picturesLoadingDiv.SetStyleAttribute("display", "none");
   24:   
   25:        var photos = (from results in doc.Descendants("photo")
   26:                 select new
   27:                  {
   28:                     PhotoId = results.Attribute("id").Value.ToString(),
   29:                     Farm = results.Attribute("farm").Value.ToString(),
   30:                     Server = results.Attribute("server").Value.ToString(),
   31:                     Secret = results.Attribute("secret").Value.ToString(),
   32:                     Title = results.Attribute("title").Value.ToString(),
   33:                     Owner = results.Attribute("owner").Value.ToString()
   34:                   }).Take(12);
   35:   
   36:      foreach (var photo in photos)
   37:      {
   38:        imageUrl = string.Format("https://farm{0}.static.flickr.com/
 {1}/{2}_{3}_m.jpg",
   39:                    photo.Farm, photo.Server, photo.PhotoId, photo.Secret);
   40:        url = string.Format("https://www.flickr.com/photos/{0}/{1}/", 
 photo.Owner, photo.PhotoId);
   41:                 
   42:        HtmlElement listItem = HtmlPage.Document.CreateElement("li");
   43:        listItem.SetAttribute("innerHTML", string.Format(LI_ITEM, url, 
 imageUrl, photo.Title));
   44:                     
   45:        ulList.AppendChild(listItem);
   46:      }
   47:   
   48:  }

So the result is this, we type in a tag to search for:

image

And the result is retrieved by the Silverlight code that then adds new <LI> items to the page.

image

 

The sample code used in this post can be downloaded here:

I can see some use for this in web applications that are currently using Ajax for this kind of processing and where there is no intention of transforming the whole interface into a Silverlight application. Using Silverlight in place of the Ajax calls could prove to be an advantage, especially if you already develop in C#.

Reproducing my example using JavaScript and AJAX is possible, but it’s another approach to achieving the same results.