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