Telerik blogs

A.k.a Deep Linking in Silverlight

Undoubtedly Silverlight offers a myriad of useful features that can enhance the user and development experience – Binding, Multithreading, Layout System, Animations to name a few. But the fact that it is in essence a browser plug-in leads to certain limitations or “hurdles” for the user experience.

Deep Linking? Why?

One such thing is the lack of Deep Linking, i.e. the browser’s address bar contains the starting page for the application but any change in its state is not reflected in the address. Some people argue that there is difference between Web Applications and Web Pages and while the latter need linking and navigation, the Web Applications can do without them.

This might has been a reasonable argument a while back but today the back-forward buttons and address bar are making their way into applications as well so fixing them in the browser is s must. Generally there are certain types of content for which paginating is the most natural and logical way to go. For example take a look at:

http://mtm.viatecla.pt/

In the real world you would like to go back after making a selection, copy the address and save it for later reference or send it to someone else. Sending links is even more important in the following video website:

http://www7.dw-world.de/silverlight_einsteinchen/popup.html

You can refer to the website but not to any particular video. 

Solving it the quick way

Silverlight comes with several classes that help us interact with the Html container of the plug-in (and therefore the browser). You might like to have a look at the HtmlPage and HtmlElement classes if you need to manipulate some Html from Silverlight. What we need though is to access the bookmark of the current page.”Bookmark” refers to everything in the address after a ‘#’. Changing the current bookmark does not refresh the page and can be used for the sub address, in other words the address in the Silverlight application.

The easiest way to do this is with the CurrentBookmark property:

1 HtmlPage.Window.CurrentBookmark = "gallery";   
2  

This way the address bar will change from

http://www.example.com to http://www.example.com#gallery. You can of course check at application startup what is the current bookmark and show the required content. End of story then?

Solving it the Script Manager way

The bookmarks help us maintain the relation between the content and the address but not the other way around. So if you change the bookmark manually then nothing would change. The back button would behave unexpectedly as well.

To put everything into a context, lets have a look at a simple example: We have a music catalog that is in essence a ListBox and a TextBlock. When you click through the list of albums, the TextBlock updates to show the songs in the album.



In this example we would be using some javascript to make the browser add the current address with its bookmark to the history stack. Although this might seem as a trivial task actually there are some cross-browser issues involved to get the manager working correctly on most browsers. Since this is beyond the scope of this article, we would be using a ready-made solution: The ScriptManager (of ASP.Net 3.5). If you do not have the ASP.NET 3.5 extensions or you would not like to use them you can have a look at  SWFAddress which offers similar functionality.

As of ASP.NET 3.5 you can set the EnableHistory property of the asp:ScriptManager and then use it to manipulate the history on the client side. In code this translates to the following:

 

1 <asp:ScriptManager EnableHistory="true" ID="ScriptManager1" runat="server"/>;   
2  

We would need to add a bit of JavaScript as well so let’s register a code-behind file as well:

1 <asp:ScriptManager EnableHistory="true" ID="ScriptManager1" runat="server">  
2     <Scripts> 
3        <asp:ScriptReference Path="~/Default.js" /> 
4     </Scripts> 
5 </asp:ScriptManager> 

We would be using the Sys.Application.addHistoryPoint(entry, title) which takes two arguments: a state object and a window title. Passing a state object is like passing a string dictionary which translates to #key=value pairs in the bookmark. I personally prefer more readable links so I have modified the history manager a little to allow for a simpler addresses of the kind: #something/something-else/. The contents of the Default.js file are now:

1 function pageLoad()  
2 {  
3         //Called when the bookmark changes.  
4         onNavigate = function(sender, state)  
5         {  
6             if(state._state.q == undefined)  
7             {  
8                 state._state.q = "";  
9             }  
10             document.getElementById("Xaml1").Content.Navigator.NavigateCallback(state.get_state().q);  
11         }  
12         //Adds a history point  
13         document.addHistoryPoint = function(entry,title)  
14         {  
15             Sys.Application.addHistoryPoint({q:entry},title);  
16         }  
17         //Serialzie override, we would just like to display a string.  
18         Sys.Application._serializeState = function(state)  
19         {  
20             return (state != null) ? state.q : "";  
21         }  
22         //The bookmark needs to translate to an object for the internal handling.  
23         Sys.Application._deserializeState = function(state)  
24         {  
25             return {q:state};  
26         }  
27         //Sign up for the navigate event:  
28         Sys.Application.add_navigate(onNavigate);  
29

A point of interest might be:

 

1 document.getElementById("Xaml1").Content.Navigator.NavigateCallback(state.get_state().q);   
2  

This is how you call managed code from JavaScript. “Xaml1” is the id of the Silverlight host, Navigator and NavigateCallback are the names of the class instance and its method that we have registered respectively:

 

1     public class Navigator  
2     {  
3         private static Navigator instance;  
4  
5         static Navigator()  
6         {  
7             instance = new Navigator();  
8             HtmlPage.RegisterScriptableObject("Navigator", instance);  
9         }  
10  
11         [ScriptableMember]  
12         public void NavigateCallback(String entry)  
13         {  
14             currentEntry = entry;  
15  
16             if (Navigate != null)  
17             {  
18                 Navigate(this, EventArgs.Empty);  
19             }  
20         }  
21     } 

In order to avoid recursions we need to agree on the following: The change of content in the Silverlight application would only be a result of a change of address callback, i.e.:


We would also introduce a static navigation manager – the Navigator class shown earlier. The xaml of the application is a simple ListBox with a DataTemplate to give a hint of credibility to the Music Catalog:

 

 

 



When you click on an album the address is changed which in turn raises a callback which changes the contents of the Tracks list.

Signing up for the events and modifying content is simple so we won’t go into details how this is done. If you are curious and would like to run the example yourself, you can get the source  here: SilverlightHistoryManager.zip
If you try to create the example yourself you may run into the following problems:

  1. If you add a Silverlight application to an ASP.NET 3.5 application the asp:Silverlight control would be referenced twice. Remove the System.Web.Silverlight reference from the project and from the aspx page.
  2. At a time you can debug either Silverlight or JavaScript but not both. If you need to debug JavaScript you can switch the Silverlight debugging off from the Web project properties:


About the Author

Kiril Stanoev

Hi, I'm Kiril and I'm the Product Manager of Telerik UI for Android, Windows Universal and Windows Phone. Feel free to ping me on +KirilStanoev or @KirilStanoev

Comments

Comments are disabled in preview mode.