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



http://forums.silverlight.net/forums/p/152282/493041.aspx
I like especially the addition of the callback that let's me cutomize the error message...
Thanks for your solution in the first place ;)
I get an error 'Content.InBrowserCloseHandler' is null or not an object.
Testet in IE8 on Windows XP.
Hope you can help.
Thanks!
Thanks, it is very good solution, but still i need to handle the Yes button, it means when user clicks on Yes button i need to put my logic(log-out process from application) before closing the browser.
Regards : Morteza