How to cancel the closing of your Silverlight application (in-browser and out-of-browser)

It’s almost two years ago when I wrote about the concept of canceling the the closing of a Silverlight application. In that era I was only trying to solve the problems that exist inside the browser. Like someone who accidently closes the tab, or types a new url in the tab of the Silverlight application. These things aren’t always a problem, but in Line of Business applications you would at least want to warn the user when he has unsaved data on the screen he’s about to close.

Nowadays we have Silverlight applications that run both in the browser and out-of-browser on the desktop. Of course some features can be different between the two versions of the application. But my intend is to have at least a feeling that 100% of the features are equal.

So I want to give my users a warning when they are exiting the application, either in-browser or out-of-browser. Even more, I want the warning to be much similar.

I want a simple message to be shown automatically. So let’s implement the ICloseHandler

public interface ICloseHandler
{
    string Message { get; set; }
    void Initialize();
}

Out of Browser

Although it might sound like a difficult task, it isn’t. Implementing a warning on exiting an out of browser app is as simple as listening to the Closing event of the MainWindow. In that occasion we simply show a messagebox and depending on the result we cancel the closing. The implementation of the ICloseHandler looks like this.

public class OutOfBrowserCloseHandler : ICloseHandler
{
    #region ICloseHandler Members

    public void Initialize()
    {
        Application.Current.MainWindow.Closing +=
            (s, e) =>
                {
                    MessageBoxResult boxResult = MessageBox.Show(
                        string.Format(
                            @"Are you sure you want to close the application?{1}{1}{0}",
                            Message, Environment.NewLine),
                        string.Empty,
                        MessageBoxButton.OKCancel);
                    if (boxResult == MessageBoxResult.Cancel)
                        e.Cancel = true;
                };
    }

    public string Message { get; set; }

    #endregion
}

 

The end result looks like this.

2011-01-30_2149

In browser

Alright, back to the browser. That’s where everything started for WPF/E, uh Silverlight. Though I wrote about a solution almost two years ago. I improved it a little bit, to make sure the solution isn’t that depended on the html that’s hosting the Silverlight application. During the initialization of the InBrowserCloseHandler the code attaches to the browser event called onbeforeunload, a piece of javascript will call back to the Silverlight application to get the message. To enable this we decorate a Silverlight method with the ScriptableMember attribute and we make sure that the InBrowserCloseHandler is registered as ScriptableObject.

public class InBrowserCloseHandler : ICloseHandler
{
    private const string ScriptableObjectName = "InBrowserCloseHandler";

    #region ICloseHandler Members

    public void Initialize()
    {
        HtmlPage.RegisterScriptableObject(ScriptableObjectName, this);
        string pluginName = HtmlPage.Plugin.Parent.Id;

        HtmlPage.Window.Eval(string.Format(
            @"window.onbeforeunload = function () {{
            var slApp = document.getElementById('{0}').getElementsByTagName('object')[0];
            var result = slApp.Content.{1}.OnBeforeUnload();
            if(result.length > 0)
                return result;
            }}",
            pluginName, ScriptableObjectName)
            );
    }

    public string Message { get; set; }

    #endregion

    [ScriptableMember]
    public string OnBeforeUnload()
    {
        return Message;
    }
}

The end result looks different for each browser. Below a few shots on the message shown by the browser. I can understand that you would want to be more in control of the look and feel of these messages. But sadly that’s not possible. However you could improve the looks of the out-of-browser message, but as I said before, I want the features between in-browser and out-of-browser to be similar, as much as possible.

Warning message of closing in Internet Explorer

Warning message of closing in Chrome

2011-01-30_2145

Combined solution

Of course I also have a combined solution, that will do the thinking about either serving the in-browser, or out-of-browser implementation.

public class PowerfullCloseHandler : ICloseHandler
{
    private readonly ICloseHandler _closeHandler;

    public PowerfullCloseHandler()
    {
        if (!Application.Current.IsRunningOutOfBrowser)
            _closeHandler = new InBrowserCloseHandler();
        else
            _closeHandler = new OutOfBrowserCloseHandler();
    }

    #region ICloseHandler Members

    public string Message
    {
        get { return _closeHandler.Message; }
        set { _closeHandler.Message = value; }
    }

    public void Initialize()
    {
        _closeHandler.Initialize();
    }

    #endregion
}

 

Usage

And for the people who are interested in how to use it. I did add a field to the App.xaml.cs for the ICloseHandler and initialized everything in the Application_Startup.

private ICloseHandler _handler;

private void Application_Startup(object sender, StartupEventArgs e)
{
    RootVisual = new MainPage();


    _handler = new PowerfullCloseHandler();
    _handler.Initialize();
    _handler.Message = "Warning you're closing...";
}