Friday, October 23, 2009

Canceling application shutdown in WPF

I hate "Are you sure" type dialog boxes. Gratuitous use of them in software makes me want to do violence to whoever thought it was a good idea to put it in. That said, there are a few times where they make sense. For example, the application I'm working on now controls an instrument that may have some expensive, hard to replace, chemistry in it. Stopping the application while the instrument is in use may result in moderate financial loss, so in this case, it does make sense to alert the user to a possible undesired outcome and make sure they really want to proceed with the action.

The WPF Window class has a Closing event that fires when the window is closed. This event has an argument containing a Cancel property with a false default value. Setting this property to true inhibits the closing of the window. When this happens on the application’s MainWindow, this closing cancellation behavior applies to the application as a whole.

Some time ago, our application had code put in to pop up an “Are you sure?” style dialog and set the event's property as appropriate. The problem was it worked for some pathways through the code but not others. The lot fell to me to investigate. There are four ways to close the application: the Alt-F4 key, the task bar's Close option, the Close button in the window's title bar and an Exit menu option. All the methods worked except the Exit menu.

Digging into the code, the Exit method menu called the Shutdown method on the Application.Current object. This seemed like a reasonable thing to do and I went looking elsewhere for the problem. After spending time verifying there wasn't anything else odd in our code I came back to this method. On a hunch, I changed this to calling the Close method on Application.Current.MainWindow. This fixed the problem. I'm not sure why, but apparently the Shutdown method does something to inhibit the normal message processing of open windows.

I know this is typical behavior and it fixes my application for now, but from a design standpoint, I'm not too sure I like this. It seems like application level shutdown code in the Window is at the wrong layer. The Application class has an Exit event that's analogous to the Window's Close event however, this does not allow canceling the operation. In my opinion, the Application class should have an Exiting event with a Cancel property on the argument, similar to the Window class' Closing event.

In any case, if your application does not call the Closing event as you expect, first check to see how it's shutdown.

(25-June-2012: Minor edit for clarity.)

5 comments:

  1. Thanks for your post it was very helpful. I was having the same problem.

    ReplyDelete
  2. Perfect. I'm not sure what resources base.exit() clears up, but it might be a good idea to add a property to your MainWindow that records !e.cancel in your MainWindow.Close event. Then, run MainWindow.Close(); if (MainWindow.ExitOK) base.Exit();

    With that said, thank you so much for finding this. You probably just saved me a few hours of unpleasant debugging and cursing.

    ReplyDelete
  3. Glad to help Anonymous.

    However, I'm not sure what you're referring to with the base.Exit() references.

    ReplyDelete
  4. I meant to say base.OnExit(e). Hopefully that clears things up.

    In App.xaml.cs:

    protected override void OnExit(ExitEventArgs e)
    {
    this.MainWindow.Close();
    if (MainWindow.CloseOK) //my property recorded as !e.cancel)
    base.OnExit(e);
    }

    ReplyDelete
  5. Ah, right. I think I see the confusion. I edited the original article to clarify I changed the Exit menu's event handler and not the application's Exit event.

    I could be wrong (it's been three years since I worked on this), but I don't think putting the call to the main window's Close method in the application's Exit event will keep the application from closing if the Window's Close event is cancelled. Once control gets to the Exit event, the application will be terminated. All you can do is set the exit code that's returned to the operating system.

    ReplyDelete

Comments are welcome but I do moderate them. This is simply to keep things wholesome for general family viewing. By default, comments will be accepted. The few things that will cause a comment to be rejected are:

1. It is too long even though it may be well-written and make interesting points. It's supposed to be a comment, not an essay. If you have that much to say, write a blog article and backlink to me.

2. It is nasty, impolite or uses language that is unacceptable.

3. It includes a a link that has a typo or is broken in some other way.

4. It should have been sent as an e-mail since it is clearly addressed to me and does not appear to have been intended for other readers.

5. It is blatantly self-promotional. This does not mean it can't be self-promotional at all, but it should add some value over an above the marketing.