Tag Archives: Silverlight

Prevent Browser Back Button for Silverlight with Confirm Dialog

One of common issues of the Silverlight, is that sometime the user accidentally clicks the browser back button. Since the  Silverlight Application hosts on a HTML page as a Frame. Any action on Silverlight does not cause the browser to keep state. So if the user clicks back button of browser, it would just go back to the page just before it reach the page hosting the Silverlight Application.

Silverlight has Navigation Framework that take care of navigate history over the pages. But some developers may decide not to use it. Or the application goes too far to add such this core functionality.

As many forums have discussed about disable browser back button. There is no direct solution and the browser should not provide it as well. It’s not fair to limit the user not to go out of the website if it really needs to. We can alternatively open the webpage that host the Silverlight Application in new window.  This way can guarantee that no previous page thus the browser back button disables itself. But this workaround is not elegant and unacceptable by my customer.

I then made another tricky workaround to confirm to the user when they clicked the browser back button.


Tested with IE8, Firefox and Chrome.

A couple of things needed to add in order to do this.

1. Insert a new page with a Frame Control at the root of application. This page does capture and confirm when the user clicked the back button.

XAML

    <Grid x:Name="LayoutRoot">
        <sdk:Frame Name="frame1" Source="/MainPage" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
            <sdk:Frame.UriMapper>
                <sdk:UriMapper>
                    <sdk:UriMapping Uri="/MainPage" MappedUri="/ActualMainPage.xaml" />
                </sdk:UriMapper>
            </sdk:Frame.UriMapper>
        </sdk:Frame>
    </Grid>

Code

public partial class MainPage : Page
    {
        private bool _doNotBlockNavigation = false;

        public MainPage()
        {
            InitializeComponent();

            this.frame1.Navigating += new NavigatingCancelEventHandler(frame1_Navigating);
            this.frame1.NavigationStopped += new NavigationStoppedEventHandler(frame1_NavigationStopped);
        }

        void frame1_NavigationStopped(object sender, NavigationEventArgs e)
        {
            ConfirmWindow confirm = new ConfirmWindow();
            confirm.Closed += (s, ea) =>
            {
                if (((ConfirmWindow)s).DialogResult ?? false)
                {
                    // Invoke Javascript function that simply go back the history
                    HtmlPage.Window.Invoke("GoBack");
                }
                else
                {
                    // Get current page from the Frame
                    var mainPage = frame1.Content as Page;
                    // Get the NavigationService of current page
                    var navigationService = mainPage.NavigationService;

                    // If the user refuses to leave the page then
                    // mark flag not to block GoBack() process
                    _doNotBlockNavigation = true;
                    if (navigationService.CanGoBack)
                        navigationService.GoBack();
                }
            };
            confirm.DataContext = "Are you really want to leave this application to go back ?";
            confirm.Show();
        }

        void frame1_Navigating(object sender, NavigatingCancelEventArgs e)
        {
            // if _doNotBlockNavigation = true then just set it back to false so the navigation works properly for the next time.
            if (_doNotBlockNavigation)
            {
                _doNotBlockNavigation = false;
            }
            else if (e.NavigationMode == NavigationMode.New && e.Uri.OriginalString == "")
            {
                // If the user presses browser's back button then temporary blocks from navigating out.
                // This would course the event NavigationStopped to be fired.
                e.Cancel = true;
            }
        }
    }

Note that the ConfirmWindow on line 15 is my custom Child Window. It behaves just like a regular confirm dialog.
Also if you did not create the project as Navigation Silverlight Project, you would need to add reference to Navigation library.


2. Add this function to the javascript block of aspx/html host page. This function will be called by line 21 of above code-snippet.

function GoBack() {
     history.go(-1);
}


3. Make sure that ActualMainPage.xaml overrides from Page not UserControl. Also do not forget to put the appropriate title for ActualMainPage.xaml, it will be shown as the HTML title. If the Title attribute is not set, it will take the current URI from UriMapper.

4. Modify App.xaml.cs to set root as MainPage instead of ActualMainPage.

this.RootVisual = new MainPage();



5. That is it! It should work properly now.

If you think this post is useful, please leave your comments.

Tagged , ,