Added FireFox, Safari, and IE7 support to the Silverlight 2 Paging Demo


With just a little code and some prompting from people on Twitter, I was able to add support for FireFox, Safari, and IE7 to the paging demo that I wrote on http://xmldocs.net/sl2paging.  Basically I added a DispatcherTimer to my page class and every 200ms I check to see if the hash has changed.  It definitely isn’t as elegant as the hashChanged event in IE8 but it works cross-browser. Here’s how I did it.

  1. I added a DispatcherTimer to my page class.
  2. In the Page Loaded event handler, I start the timer if the browser User Agent does not have MSIE 8 in it.
  3. Each time the timer ticks I compare the last hash value with the current document’s hash and if it’s different, I navigate to the new page.

Please try it out in any supported browser (IE7, IE8, FireFox, Safari) and tell me if it works for you.  Should I change the polling interval to be greater than 200ms?

Here’s the source code on MSDN Code Gallery.

Update

8/5/2008 – I wrote too soon. I got a report that this technique is not working on IE7.  I have been running the IE8 beta and I had assumed that since it worked in its IE7 emulation mode, it would work in IE7.  Back to the drawing board…

using System;
using System.Linq;
using System.Windows.Browser;
using System.Windows.Threading;
using System.Windows.Controls;
using System.Windows;

namespace SilverlightPaging
{
    /// <summary>
    /// Page Control
    /// </summary>
    public partial class Page : UserControl
    {
        string m_hash;
        DispatcherTimer m_timer = new DispatcherTimer();
        /// <summary>
        /// Default Constructor
        /// </summary>
        public Page()
        {
            InitializeComponent();
        }

        /// <summary>
        /// Page Loaded handler
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        /// <remarks>Ingnore the Code Analysis CA1811</remarks>
        private void PageLoaded(object sender, RoutedEventArgs e)
        {
            var info = HtmlPage.BrowserInformation;
            this.DataContext = info;
            
            m_hash = HtmlPage.Window.Eval("document.location.hash") as string;

            if (info.UserAgent.Contains("MSIE 8"))
            {
                HtmlPage.Window.AttachEvent("onhashchange", new EventHandler<HtmlEventArgs>(OnHashChange));
            }
            else
            {
                m_timer.Interval = new TimeSpan(0, 0,0,0, 200);

                m_timer.Tick += new EventHandler(m_timer_Tick);

                m_timer.Start();
            }

            NavigateToTab();
        }

        void m_timer_Tick(object sender, EventArgs e)
        {
            var hash = HtmlPage.Window.Eval("document.location.hash") as string;

            if (hash != m_hash)
            {
                m_hash = hash;

                NavigateToTab();
            }
        }

        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;
                }
            }

            // Get the tab items that have the hash in their Tag attribute
            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();
        }

        void OnHashChange(object sender, HtmlEventArgs args)
        {
            NavigateToTab();
        }

        /// <summary>
        /// When the tab changes, set the document.location.hash and the document.Title
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        /// <remarks>Ingnore the Code Analysis CA1811</remarks>
        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);
        }
    }
}


Comments (4)

  1. follesoe says:

    Great work!

    But it doesn’t work in IE7… The problem is that a change to document.location.hash doesn’t add a new entry to the browser history in IE7.

    The workaround is to create an iframe through script, and update it when ever you change the location.

    This post gives a good roundup of the current states of affairs when it comes to navigation history in AJAX/RIA’s:

    http://weblogs.asp.net/bleroy/archive/2007/09/07/how-to-build-a-cross-browser-history-management-system.aspx

    But again, great work! Love how this solution is all C# (just invoking JavaScript, but no JS files or "stuff" in your HTML).

    Cheers,

    Jonas 🙂

  2. Synergist says:

    Jonas,

    Darn!  I’ll look into the iframe method and see how to include it in my C# code.

    Thanks,

    Michael

  3. Michael Washington with an add-on for SL Desktop already, Mike Snow beginning Terrain tutorials and applying