Yesterday I posted here, an application, with source code about how to do forward/back navigation with IE8 and a Silverlight 1.0 application and said that I would be coming up with a Silverlight 2 (Beta 2) application that does the same type of thing - with source code.
 image

It was easier than I thought!  I created an application that uses the Silverlight 2 Tab Control and used the document.location.hash to specify the starting page.  In this case, the hash in the URL relates to the Tag property of each TabItem in the interface.  I decided to use the Tag property instead of the x:Name or Header because it had no other meaning or usage.

Here's what I did:

  1. I added the <meta/> tag to the head section of the HTML page, like before:
    <meta http-equiv="X-UA-Compatible" content="IE=8"/>
  2. In my C# code behind for the Silverlight Page I added a loaded handler for the page to both attach to the onhashchange event and to navigate to state specified in the hash:
    private void PageLoaded(object sender, RoutedEventArgs e)
            {
                HtmlPage.Window.AttachEvent("onhashchange", new EventHandler<HtmlEventArgs>(OnHashChange));
    
                NavigateToTab();
            }
  3. The OnHashChange hander would be called by IE8 either if the user navigates using the browser navigation interface or if the document.location.hash were to change programmatically.  In this case, it calls the NavigateToTab() function as well:
            void OnHashChange(object sender, HtmlEventArgs args)
            {
                NavigateToTab();
            }
  4. And then the NavigateToTab() function will navigate to the tab specified in the hash (if it's not there already):
  5. private void NavigateToTab()
            {
                var hash = HtmlPage.Window.Eval("document.location.hash") as string;
    
                if (string.IsNullOrEmpty(hash))
                {
                    return;
                }
    
                hash = hash.Substring(1);
    
                if (Tabs.SelectedItem != null)
                {
                    var selectedItem = Tabs.SelectedItem as TabItem;
    
                    if (string.Compare(selectedItem.Tag.ToString(), hash, StringComparison.OrdinalIgnoreCase) == 0)
                    {
                        // Tab is already navigated to
                        return;
                    }
                }
    
                var selectedItem1 = from item in Tabs.Items.Cast<TabItem>()
                                    where string.Compare(item.Tag.ToString(), hash, StringComparison.OrdinalIgnoreCase) == 0
                                   select item;
    
                Tabs.SelectedItem = selectedItem1.First();
            }
  6. And lastly, when the user changes the Tab, set the document.location.hash and the document.title accordingly:
  7. private void TabChanged(object sender, SelectionChangedEventArgs e)
            {
                if (Tabs == null)
                {
                    //Tabs will be null during InitializeComponent()
                    return;
                }
    
                TabItem item = Tabs.SelectedItem as TabItem;
    
                if (item == null)
                {
                    return;
                }
    
                string evalText = string.Format(System.Globalization.CultureInfo.InvariantCulture,
                    "document.location.hash=\"{0}\"", item.Tag);
    
                HtmlPage.Window.Eval(evalText);
    
                evalText = string.Format(System.Globalization.CultureInfo.InvariantCulture,
                    "document.title=\"Silverlight Paging: {0} Tab\"", item.Tag);
    
                HtmlPage.Window.Eval(evalText);
            }

I have uploaded the source code for this sample here you can try it out.  Now here is what I'd love to see from the Silverlight community: if you run this application in FireFox, you'll notice that the browser navigation doesn't work (although the deep links do).  I'd love to see a technique for doing this in FireFox and Safari so that I can update the code so that it's cross-platform.