问题
Is there a better way to handle the task of doing something after the user has chosen to exit a WinForms program than this :
[edit 1 : in response to comment by 'NoBugz] In this case there is no ControlBox on the Form, and there is a reason for putting one level of indirection in what happens when the user chooses to close the Form [/edit 1]
[edit 2 : in response to all comments as of GMT +7 18:35 January 20 ] Perhaps using fading out the MainForm is a simple illustration of what you might want do as the Application is being closed : the user cannot interact with that : it is self-evidently related to the user's decision to terminate the application. [/edit 2]
(use some form of threading ?) (implications for a multi-threaded app ?) (does this code "smell bad" ?)
// 'shutDown is an external form-scoped boolean variable
//
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
// make sure we don't block Windows ShutDown
// or other valid reasons to close the Form
if (e.CloseReason != CloseReason.ApplicationExitCall) return;
// test for 'shutDown flag set here
if (shutDown) return;
// cancel closing the Form this time through
e.Cancel = true;
// user choice : default = 'Cancel
if (MessageBox.Show("Exit Application ?", "", MessageBoxButtons.OKCancel, MessageBoxIcon.Question, MessageBoxDefaultButton.Button2) == System.Windows.Forms.DialogResult.OK)
{
// user says Exit : activate Timer, set the flag to detect Exit
timer1.Enabled = true;
shutDown = true;
}
}
Summary : In a very standard WinForms application (one MainForm launched in Program.cs in the standard way) : in the FormClosing Event handler of the MainForm :
exit immediately (triggering the default behavior : which is to close the MainForm and exit the Application) if :
a. the CloseReason is anything other CloseReason.ApplicationExitCall
b. if a special boolean variable is set to true, or
if no immediate exit : cancel the "first call" to FormClosing.
the user then makes a choice, via MessageBox.Show dialog, to Exit the Application, or Cancel :
a. if the user Cancels, of course, the Application stays "as is."
b. if the user has chosen to 'Exit :
set the special boolean flag variable to true
run a Timer that does some special stuff.
when the internal test in the Timer code detects the "special stuff" is done, it calls Application.Exit
回答1:
My suggestions, both as a developer and a user:
A very fast task
- Just do the task in the Closing event handler.
A less fast, but not incredibly slow task
- Create a non-background thread (so it's not shut down when the application exits) with the task in the Closing event handler.
- Let the application exit. Forms will go away, et cetera, but the process will keep running until the task is done.
- Just remember to handle exceptions and such in that worker thread. And make sure that things doesn't crash if the user reopens your application before that task is done.
Slower tasks
- In the Closing event handler, open a shutting-down-form and let the form itself close.
- Do the task in the/behind the shutting-down-form while displaying some friendly progress and information.
- Exit application when task is done.
Some untested example code. We are doing something similar to this in one of our applications. The task in our case is to store window properties (location, size, window state, et cetera) to a database.
private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
{
// If it wasn't the user who asked for the closing, we just close
if (e.CloseReason != CloseReason.UserClosing)
return;
// If it was the user, we want to make sure he didn't do it by accident
DialogResult r = MessageBox.Show("Are you sure you want this?",
"Application is shutting down.",
MessageBoxButtons.YesNo,
MessageBoxIcon.Question);
if (r != DialogResult.Yes)
{
e.Cancel = true;
return;
}
}
protected override void OnFormClosed(FormClosedEventArgs e)
{
// Start the task
var thread = new Thread(DoTask)
{
IsBackground = false,
Name = "Closing thread.",
};
thread.Start();
base.OnFormClosed(e);
}
private void DoTask()
{
// Some task to do here
}
回答2:
I don't see anything "wrong" with this. My only recommendation would be to let the user know that the program is "shutting down" by raising a non-modal window or perhaps a notification toaster above the system tray.
回答3:
A couple of minor points:
I'd question the need for a timer: as the application is exiting anyway, and the user won't therefore expect the UI to be responsive, why not simply do the clean-up code on the UI thread? As suggested by @Dave Swersky, a little notification window during cleanup would be polite.
What happens if the application exit is triggered by a Windows shutdown or a user logoff?
来源:https://stackoverflow.com/questions/2096318/delay-application-close-best-practice