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.)


Anonymous said...

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

Anonymous said...

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.

Harley Pebley said...

Glad to help Anonymous.

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

Anonymous said...

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

In App.xaml.cs:

protected override void OnExit(ExitEventArgs e)
if (MainWindow.CloseOK) //my property recorded as !e.cancel)

Harley Pebley said...

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.