Implementing Deeplinking in Silverlight

 

This article is now outdated. Silverlight 3 now has deeplinking built in. There might be other articles on this but this is the only one I could find on this topic:  http://programwith.net/2009/03/23/Silverlight3NdashDeepLinking.aspx


Version: Silverlight 2 Beta 2 / .NET 3.5 SP1

See source code links at the bottom of this post.

The following post is an attempt at implementing deep linking in Silverlight. Deeplinking allows users to use the browser back and forward buttons to switch between different “states” of the application and also allows bookmarking a certain state of the app.  It provides a great UX in scenarios like viewing a slide show, paging through a document etc.  IMHO, I think deeplinking should be baked into Silverlight by default.

To implement this, we use the System.Web.Extensions assembly in .NET 3.5 SP1 Beta which adds native support for navigating forward and backward through the browser history stack and also allows bookmaking. We will add some custom code to our Silverlight application and host it inside an aspx page so that it can take advantage of the new features of the assembly or more specifically the JavaScript code in this assembly.

As a quick refresher, in the context of an ASP.NET AJAX application, we add new entries to the browser history stack by calling Sys.Application.addHistoryPoint(state, title) where state is a StringDictionary  containing the state we wish to store and title is the page title.

The Sys.Application.navigate event is raised anytime the user clicks the browser back or forward button. To restore the state of the page, we subscribe to this event and call e.get_state() inside our event handler. You can read more on this topic here.

The idea here is to have our Silverlight application make a javascript call to addHistoryPoint anytime we wish to store the state of the application in the browser history. We then subscribe to the JavaScript Sys.Application.navigate event and change the state of our application when this event is raised.

We start by creating Silverlight application using Blend 2.5 June 2008 preview or VS 2008. In the App.cs class, we add a helper method for making the call to addHistoryPoint like so (FYI, HtmlPage.Window.Eval method performs the equivalent of making a JavaScript eval method call):

   1: /// <summary>
   2: /// Call to Sys.Application.addHistoryPoint
   3: /// </summary>
   4: public void AddHistoryPoint(string stateKey, string stateValue, string title)
   5: {
   6:     string addHistoryScript = "Sys.Application.addHistoryPoint({{ {0}:'{1}' }}, '{2}');";
   7:     HtmlPage.Window.Eval(string.Format(addHistoryScript, stateKey, stateValue, title));
   8: }

 


The next step is to subscribe to the JavaScript Sys.Application.navigate event from our Silverlight managed code. To make our managed code accessible through javascript, we have to register our class as a scriptable object and then make our method accessible by marking it with the Scriptable attribute, Read more on this topic here.

As shown below, HandleNavigate is marked as a scriptable member which exposes it to JavaScript.

   1: /// <summary>
   2: /// Events
   3: /// </summary>
   4: public event EventHandler<StateEventArgs> Navigate;
   5:  
   6: /// <summary>
   7: /// Handler for Sys.Application navigate event
   8: /// </summary>
   9: [ScriptableMember]
  10: public void HandleNavigate(ScriptObject state)
  11: {
  12:     if (Navigate != null)
  13:     {
  14:         Navigate(this, new StateEventArgs() { State = state });
  15:     }
  16: }

In the code below, we register our class as a scriptable object (Line 13). We also dynamically load a Javascript script block which creates a helper JavaScript function that subscribes to the navigate event. The helper function then makes a call to our silverlight HandleNavigate method (Line 17). The last method in the script block is a call to __navigateHandler which is for restoring the state of the page when the page loads for the first time or through a bookmark.

   1: private void OnStartup(object sender, StartupEventArgs e)
   2: {
   3:     // Load the main control here
   4:     this.RootVisual = new Page();
   5:     InitScripts();
   6: }
   7:  
   8: /// <summary>
   9: /// Register event hander through proxy method
  10: /// </summary>
  11: private void InitScripts()
  12: {
  13:     HtmlPage.RegisterScriptableObject("App", this);
  14:     string initScript = @"
  15:         var __navigateHandler = new Function('obj','args','document.getElementById(\'appId\').content.App.HandleNavigate(args.get_state())');
  16:         Sys.Application.add_navigate(__navigateHandler);
  17:         __navigateHandler(this, new Sys.HistoryEventArgs(Sys.Application._state));
  18:      ";
  19:     HtmlPage.Window.Eval(initScript);
  20: }

With this in place, we can subscribe to the Navigate event from any class and use the state information to make changes to the state of our application.

   1: App app = (App)Application.Current;
   2: app.Navigate += new EventHandler<StateEventArgs>(app_Navigate);
We call the AddHistoryPoint method to store the state of the app when needed as shown below.
   1: App app = (App)Application.Current;
   2: string pageTitle = (activeSlideIndex == -1) ? "Home" : "Slide " + activeSlideIndex;
   3: app.AddHistoryPoint("index", activeSlideIndex.ToString(), pageTitle);

The last step involved is to create an ASP.NET AJAX application, add a ScriptManager control, set the EnableSecureHistorState to false (so that our URL is human readable) and finally add a Silverlight control with the source set to the xap file.

   1: <asp:ScriptManager ID="ScriptManager1" runat="server" EnableHistory="true" EnableSecureHistoryState="False">
   2: </asp:ScriptManager>
   3: <div id="slcontent">
   4:     <asp:Silverlight ID="appId" runat="server" Source="~/ClientBin/SilverlightHistory.xap"
   5:         MinimumVersion="2.0.30523" Width="800" Height="600" BackColor="Black" />
   6: </div>


You can download the sample project here or here (This is experimental beta code - use at your own risk).
Things you can try:

  1. Clicking on the links in the right menu adds history points to the browser stack.
  2. You can use the browser back and forward buttons to walk the stack.
  3. You can bookmark a URL and return to it (example: localhost:9999/SilverlightHistory_Web/Default.aspx#index=2 will load the app with  the third slide visible)

Another topic that has been making headlines recently is RIA SEO. Here are three articles on that:

  1. Search for Rich Internet Applications
  2. SEO for Ajax and Silverlight Applications
  3. SWF searchability FAQ
  4. Improved Flash indexing

Comments are always welcome.

No Comments