Integration of browser’s unloading with Silverlight

It happens very often that people, the users of your application loose their changes because the accidentally closed a browser window or browser tab. Specially because all browsers now support tabs, which is not something all non-technical users completely understand.

There is however something you can use to partly overcome this issue. In the year 2004 there was an article about this on 4guysfromrolla.com. This article explains excellently how to make use of the window.onbeforeunload event in Javascript.

I thought about this a few minutes and got the following example on how to integrate this inside your Silverlight application.

First of all I wanted this window.onbeforeunload Javascript event to call a method inside my Silverlight application. So I wrote a small method that can be called inside Silverlight.

    1 [ScriptableMember]

    2 public string OnBeforeUnload()

    3 {

    4     return "Warning you're closing...";

    5 }

Besides that it also needs to be registrated in Javascript.

const string scriptableObjectName = "Bridge";

HtmlPage.RegisterScriptableObject(scriptableObjectName, this);

So now we have a method that can be called from Javascript. Let’s start listening to this Javascript event from Silverlight.

string pluginName = HtmlPage.Plugin.Id;

HtmlPage.Window.Eval(string.Format(

    @"window.onbeforeunload = function () {{

                var slApp = document.getElementById('{0}');

                var result = slApp.Content.{1}.OnBeforeUnload();

                if(result.length > 0)

                    return result;

            }}", pluginName, scriptableObjectName)

    );

This does work. But do we want to always show a message? Or do we only want to show the message if it’s relevant? I think the choice will be the last, at least for me it will. So what about an IsDirty check? I created a very simple non-real one. Because of the sake of the article, it’s about the message when closing, right?

private bool isDirty = true;

public bool IsDirty

{

    get

    {

        bool tempDirty = this.isDirty;

        this.isDirty = false;

        return tempDirty;

    }

}

Let’s combine this with our OnBeforeUnload method in Silverlight.

[ScriptableMember]

public string OnBeforeUnload()

{

    if (IsDirty)

        return "The page is dirty. Are you sure you want to close?";

    return string.Empty;

}

When you take a close look at my implementation of IsDirty, you’ll see it’s only dirty the first time, the second time it’s not. Now when you try to exit the page by one of the following ways, you’ll get this message:

  • By closing Tab;
  • By closing Window;
  • By changing to a different url;

image

When you press Cancel you will actually stay on the page. The thing is we only have influence on the text in the middle. I have been thinking about different solutions, by using for example a dialog in Silverlight, but sadly this is just not possible. The function window.onbeforeunload only accepts a message that it can show. When nothing is return on the event, it just closes.

For your convenience the complete code of Page.xaml.cs.

    1 public partial class Page : UserControl

    2 {

    3     private bool isDirty = true;

    4     public Page()

    5     {

    6         InitializeComponent();

    7         RegisterOnBeforeUnload();

    8     }

    9 

   10     public void RegisterOnBeforeUnload()

   11     {

   12         //Register Silverlight object for availability in Javascript.

   13         const string scriptableObjectName = "Bridge";

   14         HtmlPage.RegisterScriptableObject(scriptableObjectName, this);

   15         //Start listening to Javascript event.

   16         string pluginName = HtmlPage.Plugin.Id;

   17         HtmlPage.Window.Eval(string.Format(

   18             @"window.onbeforeunload = function () {{

   19                 var slApp = document.getElementById('{0}');

   20                 var result = slApp.Content.{1}.OnBeforeUnload();

   21                 if(result.length > 0)

   22                     return result;

   23             }}",pluginName,scriptableObjectName)

   24             );

   25     }

   26 

   27     [ScriptableMember]

   28     public string OnBeforeUnload()

   29     {

   30         if (IsDirty)

   31             return "The page is dirty. Are you sure you want to close?";

   32         return string.Empty;

   33     }

   34 

   35     public bool IsDirty

   36     {

   37         get

   38         {

   39             bool tempDirty = this.isDirty;

   40             this.isDirty = false;

   41             return tempDirty;

   42         }

   43     }

   44 }

  • Gravatar ToMaHaWk March 23rd, 2009 at 16:31
    On line 19 : document.getElementById(’{0}’);

    should be document.getElementById('{0}');
  • Gravatar Mark Monster April 3rd, 2009 at 14:20
    Hi ToMaHaWk,

    The thing you're mentioning has something to do with the editor changes ' into ’.

    Thanks.
  • Gravatar Jason Jarrett June 17th, 2009 at 21:44
    Great post. Only issue is the single quote in the javascript copied the wrong character... Worked perfectly for my needs...

    Thanks
  • Gravatar kapil bhavsar August 21st, 2009 at 09:45
    Hi,

    Nice and Quick work around.... but how to do this if Application is Out Of Browser
  • Gravatar Mark Monster August 21st, 2009 at 09:58
    Hi Kapil,

    I don't know about any solution in the Out-of-Browser case, yet.
  • Gravatar Adam September 16th, 2009 at 16:10
    Hi Mark I have been trying to get this going in Silver 3 but am having difficulties with HtmlPage.Plugin.Id as it returns blank, any ideas?

    Thanks

    Adam
  • Gravatar Krishn September 24th, 2009 at 16:31
    Hello Adam,

    I got the same blank one until I realised how silly it was - Did you actually give a ID to the main page div - that will solve it.

    Hi Mark,

    great article - but am facing yet another problem. I need to call logout when browser closes - if I send a logout message on beforeunload function it is blocked until the string is returned :(

    I was trying to return a blank string - problem is if the user presses 'cancel' i cannot logout.

    Is there a way to catch if 'ok' or 'cancel' is pressed or is there a way to not show 'cancel' button at all.

    Any pointers will be very useful

    Cheers
    Krish
  • Gravatar Muneer October 19th, 2009 at 08:56
    Hi,
    How Can we Know whether Browser is Closed are Refreshed in Silverlight Please Help.
    Coz for Both Refresh and Close OnbeforeUnload is Executing. Then How Can we Know whether
    Browser is Closed are Refreshed.i wnt to Clear my Isolatedstorage Settings once the Browser is Closed.
    Thanks and Regards,
    Muneer
  • Gravatar Mark Monster October 19th, 2009 at 09:30
    @Muneer,

    I'm not aware of anyway to find out the difference between close and refresh.

    -
    Mark Monster
  • Gravatar Britney Spears November 12th, 2009 at 12:01
    Hi Mark,

    What a great idea! Thanks for sharing your code. It works like a charm. Of course only after I gave my silverlight object a ID. Thanks Krish! I was wondering what went wrong.
  • Gravatar Lance July 8th, 2010 at 11:50
    I'm wondering what part of the code here is calling the confirm? I want to do something like if the user clicks on OK, it's going to call another silverlight method before closing the browser.
  • Gravatar Neha July 19th, 2010 at 06:47
    Thanks Mark, I tried it. I have set id, still I am getting following exception, "Microsoft JScript compilation error Invalid character" on this line HtmlPage.Window.Eval(string.Format(........));

    I am using SL4, IE and VWD2010. Any idea why? Thanks.

    Regards
    Neha
  • Gravatar Neha July 20th, 2010 at 05:53
    Got it right, was a typo. G8 code, working perfectly!! :)
  • Gravatar Mark Monster July 20th, 2010 at 08:17
    @Lance, I agree it would be nice if this was possible, but sadly there's no way to interact with the result.
  • Gravatar jamal September 13th, 2010 at 15:18
    hi,
    just like what lance want, i would also like a notification to the silverlight app if user selected OK.
  • Gravatar eric xiao December 9th, 2010 at 05:47
    my MainPage.xaml like this:
    <!--Navigate toolBar Here-->


    i want to do like this:
    when user update something on page supportingDocumentManager and then jump to QAManager give save message.

    the mainpage.xaml :
    private void SplitButton_Click(object sender, System.Windows.RoutedEventArgs e)
    {
    RadRibbonSplitButton radRibbonSplitButton = sender as RadRibbonSplitButton;

    if (radRibbonSplitButton != null)
    {
    string boardName = radRibbonSplitButton.Name.ToString();
    NavigateTo(boardName);
    }
    }

    private void NavigateTo(string pageName)
    {
    App.currentSelectedMenu = pageName;
    this.navigateFrame.Navigate(new Uri(pageName, UriKind.Relative));
    }
    how can i to do it
  • Gravatar Mark December 13th, 2010 at 21:45
    Hi Mark,

    If I can get this to work, you've provided me with a perfect solution to my SL4 app problem I've been trying to solve off and on for months.

    The trouble with my implementing your code is similar to one that another developer posted to you in a separate blog. My plugin name/ID is always returning an empty string, even though it appears I've followed your steps to a T (but must be missing something, unless it's possibly because I'm using Chrome 8...?).

    Here's my .html and .aspx files' body definition:










    <a href=" http://go.microsoft.com/fwlink/?LinkID=149156&amp;v=4.0.50401.0" rel="nofollow">

    </a>





    And here's my SL4/C# code:

    public void RegisterOnBeforeUnload()
    {
    //Register Silverlight object for availability in Javascript.
    const string scriptableObjectName = "Bridge";
    HtmlPage.RegisterScriptableObject(scriptableObjectName, this);
    //Start listening to Javascript event.
    string pluginName = HtmlPage.Plugin.Id;
    HtmlPage.Window.Eval(string.Format(
    @"window.onbeforeunload = function () {{
    var slApp = document.getElementById(’{0}’);
    var result = slApp.Content.{1}.OnBeforeUnload();
    if(result.length &gt; 0)
    return result;
    }}", pluginName, scriptableObjectName)
    );
    }

    This line:

    string pluginName = HtmlPage.Plugin.Id;

    Simply always returns ""

    Hope you (or someone else?) can help me soon - thanks in advance!!
  • Gravatar Chad March 25th, 2011 at 16:41
    Getting the silverlight app by id doesn't want to work for me, but getting the div that contains the silverlight app by id does. So, I've changed the javascript to:

    System.Windows.Browser.HtmlPage.Window.Eval(
    @"window.onbeforeunload = function ()
    {
    var host = document.getElementById('silverlightControlHost');
    var result = host.firstChild.Content." + scriptableObjectName + @".OnBeforeUnload();

    if (result.length > 0)
    return result;
    }");

    Thanks for this great article.
  • Gravatar andy April 14th, 2011 at 08:08
    Great article, thank you very much, Mark, that's just I want to do.



  • Gravatar Jiang Pei-Rong June 15th, 2011 at 13:16
    Thank you!!!
  • Gravatar Jiang Pei-Rong June 15th, 2011 at 13:38
    I have used the MVVM Framework, I want to know how to pate these code into my MVVM?
  • Gravatar Atmos June 28th, 2011 at 09:22
    I don't really understand all of the code.

    SL4, VS2010

    I'm using Chad alteration to the code, but I'm getting an error

    Line: 4
    Error: Unable to get value of the property 'Bridge': object is null or undefined.

    Great article, its given me an idea of how to implement it for stuff that usually only works for asp.
  • Gravatar Bone February 8th, 2012 at 09:51
    Hi, I have the very same problem as yours, and I'm using your method.

    But my "SaveUserConfig" method in Silverlight is asyn, it doesn't get finished before the html page is closed, and the operation gets cancelled/aborted, so the user's config doesn't get saved.

    Any idea of how to solve that? Thanks a lot!
  • Gravatar Bone February 8th, 2012 at 09:54
    If you can help me, please tell me at bone.chang@gmail.com, thank you in advance!
  • Gravatar Mark Monster February 9th, 2012 at 10:10
    You can better regularly save the user config, or as soon as it changes instead of on closing of the application. HTH, Mark Monster
  • Gravatar Nadia February 27th, 2012 at 19:41
    Hi.

    I am with doubts. You have the project to send me?

    Thank you.
  • Gravatar Nadia February 27th, 2012 at 21:27
    I need to decremente an application variable when closing browser. How to make?
Gravatar