views:

278

answers:

2

I've made a rather complex Silverlight 4 out-of-browser application. One of my main view models adds an event handler to the Application.Current.MainWindow.Closing event. This works fine when the application is initially run. It is able to cancel the close operation.

However, sometimes after performing operations like showing and closing a ChildWindow, the MainWindow's Closing event is no longer calling my handler.

In the debugger, I added a watch to the MainWindow's underlying closing event delegate. It's not null before showing the ChildWindow. Then sometimes after the ChildWindow is closed the delegate is null. This is explains why my handler is not called any more. But why is this delegate getting nulled? And why is it only happening occasionally? My application is not unbinding my event handler at any point.

This is the delegate I'm watching:

System.Windows.Application.Current.MainWindow.m_closingEvent

Other stuff: I'm using Caliburn Micro

+2  A: 

Instead of hooking to the event, why not register a service instead? Create a class that implements IApplicationService and IApplicationLifetimeAware. The latter gives you an "onexiting" and "onexited" pair of events. You place the service in the application by pointing to it in a section called in your App.xaml. I've used this for many projects and never had an issue with the exiting methods not being called.

Jeremy Likness
Thanks for the answer, but I need the ability to cancel the exiting event (after prompting the user). That interface doesn't seem to provide an option for that.
Andrew Davey
+1, Not because its an answer to the problem at hand but because its just a tidy thing to do. Also its just too easy to forget App lifetime objects so its nice to be reminded, you don't have to do everything in App Start and Exit.
AnthonyWJones
+1, been looking for a neat solution that that problem. Cheers.
Enough already
+2  A: 

I had the exact same problem. We have a large silverlight application running OOB.

For some reason the m_ClosingEvent was nulled after running for a while. I have not been able to find the cause of this issue but I think it may have something to do with us changing the root visual or all the child windows we show.

I´m using a class ApplicationWrapper.

public class ApplicationWrapper : IApplicationWrapper
{
  public void Initialize()
  {
    HookCloseEvent(true);
  }
  private void HookCloseEvent(bool hook)
  {
    if (hook && IsRunningOutOfBrowser)
    {
      Application.Current.MainWindow.Closing += OnClosing;
    }
    else
    {
      if (IsRunningOutOfBrowser)
      {
        Application.Current.MainWindow.Closing -= OnClosing;
      }
    }
  }
  private void OnClosing(object sender, ClosingEventArgs e)
  {
    InvokeClosing(e);
  }

... etc.. 
}

And the InvokeClosing method was never called. But when I changed it to

public class ApplicationWrapper : IApplicationWrapper 
{
  private Window _mainWindow;

  public void Initialize()
  {
    if(IsRunningOutOfBrowser)
    {
      _mainWindow = Application.Current.MainWindow;
    }
    HookCloseEvent(true);
  }

  private void HookCloseEvent(bool hook)
  {
    if (hook && IsRunningOutOfBrowser)
    {
      _mainWindow.Closing += OnClosing;
    }
    else
    {
      if (IsRunningOutOfBrowser)
      {
        _mainWindow.Closing -= OnClosing;
      }
    }
  }

  private void OnClosing(object sender, ClosingEventArgs e)
  {
    InvokeClosing(e);
  }

... etc... 
}

The m_ClosingEvent isn´t nulled.

So, try to just store the "initial" MainWindow in a field and check if that solves your problem.

Truls Clauss
I just tried this out and it seems to work! I will need to test on a few more machines to be sure, but it looks like you've solved it (and solved it simply!) Thank you you so much! I'll mark this as the answer and award your bounty soon.
Andrew Davey