Back button press when using webbrowser control in WP7

Jaime Rodriguez
On Windows Phone, Windows Presentation Foundation, Silverlight and Windows 7

Back button press when using webbrowser control in WP7

Rate This
  • Comments 3

If you have a Windows Phone application that hosts the WebBrowser control and allows or encourages the users to navigate within the WebBrowser control’s space,  you are going to run into the 5.2.4.b certification requirement:

                “Pressing the Back button must return the application to the previous page”

This requirement is easy to understand for PhoneApplicationPages; but for HTML, I am still undecided on how intuitive it would be to a user that is navigating inside a WebBrowser control. 
Most of the apps I have been involved with that use the WebBrowser control, do not encourage navigation ( they show small HTML inline) or show HTML it in a popup that only has a close button on the top right, so I have never worried about integration between WebBrowser and back button.  It was always a “Back” closes the transient popup.
That said, I saw a news reader  app last week that had a full screen WebBrowser, and a back and forward button in the appbar, this felt weird ( to see a back button in the appbar) I thought the hardware back button should integrate with this “perceived” navigation stack .  This got me thinking about 1) how hard it is to code and 2) how error prone it would be.

Yesterday, I quickly threw in an HTML page that I felt would cover most linking scenarios. It has regular navigations (A HREF) , shortened URLs (that lead to redirects), navigates using script, and has fragment navigations.   I then created a project with WebBrowser to integrate with back button.

Here is my one-hour exploration:

1) Listen to WebBrowser.Navigated event;  keep track of the pages that have been visited.

Stack<Uri> history= new Stack<Uri>();
Uri current = null; 
private void WebBrowser_Navigated(object sender, System.Windows.Navigation.NavigationEventArgs e)
{
    Uri previous = null;           
    if (history.Count > 0)
        previous = history.Peek();

    // This assumption is NOT always right. 
    // if the page had a forward reference that creates a loop (e.g. A->B->A ), 
//
we would not detect it, we assume it is an A -> B -> back () if (e.Uri == previous) { history.Pop(); } else { if (current != null) history.Push(current); }
current = e.Uri; } 2) Listen to OnBackKeyPress on the page. If the WebBrowser has a navigation stack, cancel the backkeypress and navigate within the webbrowser control’s stack.
protected override void OnBackKeyPress(System.ComponentModel.CancelEventArgs e)
{
    base.OnBackKeyPress(e);

    if (!isPerformingCloseOperation) 
    {
        if (history.Count > 0)
        {                        
                Uri destination = history.Peek();
                webBrowser.Navigate(destination);
                // What about using script and going history.back? 
                // you can do it, but 
//
I rather use that to keep ‘track’ consistently with our stack e.Cancel = true; } } }

As you can see, code is trivial, but it has an issue I did not solve.  I can’t tell the difference between:

  1. Navigate(a) –> Navigate ( b) –> back ();
  2. Navigate(a) –> Navigate ( b) –> Navigate ( a ) ;

I tried letting the browser do the work, and peeking at history.length (you can see the code in the sample, commented out), but that does not help since the sequence #1 above leads to a length of 2, instead of a length of 0.    Since that does not solve it (and I don’t see a different way, is this good enough?   I think it is, because:

  • I believe scenario #2 above does not happen a lot. So, I optimized for #1, it does the trick because worst case it gets user out quicker.
  • If you are concern on #2, you can add an ‘escape valve’.  I put a close button on the top right hand of the page, so the user can close the browser page if they want to; you could also use appbar if your page has one.

Unless I missed another major gotcha, I think this does nicely.   It is intuitive to the user in most typical navigations. It lets them out of the browser page (history.length was getting stuck since it never decreased to 0).    I also asked internally to the Microsoft folks building apps, and the ones that integrate back button and webbrowser navigation are using the same pattern, and have not had any complaints from users. 

To conclude, here is my personal advise/summary:

  • Not all apps need this integration. If you are just showing a single page, and don’t want to encourage navigation, use a UX pattern that hints the user that the UI is transient – make it look like a popup, put a close button on the popup’
  • If you have an app that does encourage navigation in the browser,  consider the pattern above. Don’t do it because of the certification requirement, do it because it is intuitive to the user. That is why the requirement exists. 
  • If you feel that your app does not fall under one of the above, then do ask for an exception to the certification team. That said, I emphasize you should thrive to create an intuitive experience for your user. I think the two options do that best so I recommend them –as opposed to the excuse/exception route-.

Sample code is here.

Happy Windows Phone coding!

If you want to keep up with Windows Phone on a more frequent basis, subscribe to my Windows Phone question of the day RSS feed.

  • Thanks for that Jamie.  That was on my list of things to look into but thankfully you've beaten me to it :-)

  • Thanks for Sharing.

  • Thanks for sharing Jaime.  I was getting tired of my on-line hosted help "backing" back to the app instead of the previous page.  Although my case is specific, I modified your code a little so that I could return to my "Index" page (from link on all other pages) and intentionally lose the history in-between.  It also "appears" to fix the round-robin you talked about.  If you know of any use cases, where this screws up, I'd appreciate if you leave a message here in your Comments.   Thanks.

    private List<Uri> _history = new List<Uri>();

    protected override void OnBackKeyPress(CancelEventArgs e)

    {

       base.OnBackKeyPress(e);

       if (_history.Count > 1)

       {

           // Let web_Navigated clean up.

           web.Navigate(_history[1]);

           e.Cancel = true;

       }

    }

    private void web_Navigated(object sender, NavigationEventArgs e)

    {

       int indx = _history.IndexOf(e.Uri);

       if (indx >= 0)

       {

           // Remove all up to i.

           for (int i = 0; i < indx; i++)

               _history.RemoveAt(0);

       }

       else

           _history.Insert(0, e.Uri);

    }

Page 1 of 1 (3 items)