ShowDialog in Closing-Event - c#

If the user closes the Application a Save-File-Message have to be shown (to be sure that he wants to discard the changes of edited files).
to implement this, i have a menuitem with a command-binding (without key-gesture):
private void Command_Exit(object sender, ExecutedRoutedEventArgs e)
{
Application.Current.Shutdown();
}
the mainwindow has a closing-event. in this event i check if there unsaved files. if yes, the savedialog has to be opened (to choose, which files have to be saved):
private void Window_Closing(object sender, CancelEventArgs e)
{
if (sdl.Count() > 0)
{
SaveDialog sd = new SaveDialog();
IEnumerable<Doc> close = sd.ShowDialog(this);
if (close == null)
e.Cancel = true;
else
foreach (Doc document in close)
document.Save();
}
}
in this ShowDialog-Method (implemented in my SaveDialog-Class) i call
bool? ret = ShowDialog();
if (!ret.HasValue)
return null;
if (!ret.Value)
return null;
The problem is:
If i use the Alt+F4-Shortcut to close the Application (default-behaviour of the mainwindow) it works and i get the savedialog if there are unsaved files. but if i close the application by executing the Command_Exit-Method, the Method-Call
bool? ret = ShowDialog();
returns null and the dialog does not appear.
If i assign the Alt+F4 KeyGesture to the CommandBinding, the problem is switched: Executing Command_Exit works well but Alt+F4 Shortcut not.
What is the reason that the ShowDialog()-Method works not in both cases and how to fix it?

The Application.Current.Shutdown route allows you to listen for the shutdown request by handling the Exit event as detailed here:
http://msdn.microsoft.com/en-us/library/ms597013.aspx
It doesn't detail how it closes windows, so I wouldn't necessarily be convinced that the closing event handler would fire before it closes the application.
The other very standard way to shut the application down is to close the main window (the one shown at the very beginning). This would likely be the Window.Close method, if you are in the context of the window already, just call Close(). This will then hit the closing event handler.

Your Command_Exit implementation is wrong. Application.Current.Shutdown() means that the application is already shutting down, which can prevent the dialogs from opening.
You should implement the command other way: in the command you should ask your business logic if it's safe to shutdown, and issue Application.Current.Shutdown() only in that case. Otherwise, you should ask the business logic to start the shutdown sequence, which would in turn save the open files, and issue a Shutdown upon completing save operations.
Moreover, you should trigger the same routine when the user tries to close the main window (that is, on its Window.Closing).

Related

How to execute code when the user close the session?

i am writing an application which use microscope.
When the user close the application, the application turned off the microscope.
Is there a way to execute this code when the application is closed because the user close the windows session ?
Regards,
You can use the FormClosing event to catch a form closing action, and perform tasks before closure, or prevent it from closing at all. Here is an example I use regularly.
private void Form_FormClosing(Object sender, FormClosingEventArgs e)
{
//Do stuff here:
if (prevent_close)
{
//or cancel the closing here:
e.Cancel = true;
}
}

C# OpenFileDialog spawns many threads, and application exit hanging

Lately I have been having problems with my application not shutting down properly. After it has been told to exit, when I look in the Task Manager the process is still running, and I am unable to kill the process.
Suddenly I realized a strange pattern. The shutdown problem only appeared if I had opened a OpenFileDialog anytime when the application was running. I debugged a bit and saw that some threads did not shut down after the application should have exited. Also, to my surprise, when I invoked OpenFileDialog.ShowDialog(), it spawned a lot of threads (See the pictures below). The threads are alive throughtout the lifetime of the application.
Why does OpenFileDialog spawn so many threads? And why are they not closed after the file dialog is closed.
How does the OpenFileDialog problem relate to my shutdown problem...?
Threads just before openFileDialog.ShowDialog():
Threads while the dialog is open:
Threads right after openFileDialog.ShowDialog() has returned:
Threads hanging after the application has been shut down:
Code for opening the dialog:
private void startAllSequenceToolStripMenuItem_Click(object sender, EventArgs e)
{
OpenFileDialog ofn = new OpenFileDialog();
DialogResult result = ofn.ShowDialog();
if (result == DialogResult.Cancel)
return;
MessageBox.Show("do stuff");
}
I search the web for this and found nothing but I fixed by issue by calling Dispose. Code below:
private void startAllSequenceToolStripMenuItem_Click(object sender, EventArgs e)
{
OpenFileDialog ofn = new OpenFileDialog();
DialogResult result = ofn.ShowDialog();
if (result == DialogResult.Ok)
{
MessageBox.Show("do stuff");
}
// This one line seems to allow my application to exit cleanly in debug and release.
// But I don't instantiate a new object.
// I used the control on the form and called Dispose from form_closing.
ofn.Dispose();
}
This thread OpenFileDialog/c# slow on any file. better solution? has some half decent answers. All in all, as a last resort check with ProcExp from sysinternals. Also, is it only slow in the debugger? If so I wouldn't worry about it since it doesn't affect your users. Just make sure to isolate from your other code so your team is not constantly tripping over it in the debugger since it is slow.

Understanding when/how events are sent

I have code like this:
// Create event handler delegate
symbolReader.ReadNotify += new EventHandler(symbolReader_ReadNotify);
When a barcode is scanned on my handheld device then symbolReader_ReadNotify is called.
This is a simplified version of that method:
/// <summary>
/// Event that fires when a Symbol scanner has performed a scan.
/// </summary>
private void symbolReader_ReadNotify(object sender, EventArgs e)
{
ReaderData readerData = symbolReader.GetNextReaderData();
// If it is a successful scan (as opposed to a failed one)
if (readerData.Result == Results.SUCCESS)
{
// Setup the next scan (because we may need the scanner
// in the OnBarcodeScan event below
Start();
// Get the handle of the window that the user was on when the scan was sent.
IntPtr handle = CoreDLL.GetTopWindow();
// If we have a barcode scanner method for this window then call that delegate now.
if (_scanDelegates.ContainsKey(handle))
{
Action<BarcodeScannerEventArgs> scanDelegate;
// Get the method to call for this handle
// (previously added to the _scanDelegates dictionary)
bool delegateRetrieved = _scanDelegates.TryGetValue(handle, out scanDelegate);
if (delegateRetrieved && (scanDelegate != null))
scanDelegate(e);
}
}
}
That works fine most of the time. But when the call to scanDelegate opens a new window that also needs to accept scans the event (symbolReader.ReadNotify) does not fire (when the scan is done on that window). But once the window closes (and scanDelegate(e) returns) the event does fire (but now I route it to the wrong window.
Is there someway to tell the app to send the event? Does it work like windows messages (i.e. there is a way to flush the messages) or is it just the Symbol library that is failing to send the event until it is too late?)
The one thing I have tried is calling Application.DoEvents in a loop in the window that is opened. But that does not seem to work.
Note: This is a Compact Framework app, but I don't think this is a Compact Framework issue, so I am not tagging it with Compact Framework.
Any advice to get the event to fire when the scan happens (like it does when it is not a nested scan) would be great!
Does the scanDelegate(e) open a the new window as a Dialog? If so it'll block the event from raising again untill it's closed because it's called (opened) from within the same eventhandler.
You can work around it by either delaying the opening untill after the event is handled, not opening it as a dialog or by opening it on a new thread (or use begininvoke on the delegate)

delay Application Close best practice?

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
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
}
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.
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?

Close another process when application is closing

I have a C# winform application that during its work opens another Winform process. The other process has UI of its own.
When I close the parent application, I want the other application to be closed automatically.
How can I achieve that?
Thanks
If you are using Process.Process there is the CloseMainWindow method. If you keep a reference to the object you can use it later.
Here's the relevant page in the MSDN
and the relevant code:
// Close process by sending a close message to its main window.
myProcess.CloseMainWindow();
// Free resources associated with process.
myProcess.Close();
There are several different options. I would suggest that you have your application keep track of the processes that it starts:
private Stack<Process> _startedProcesses = new Stack<Process>();
private void StartChildProcess(string fileName)
{
Process newProcess = new Process();
newProcess.StartInfo = new ProcessStartInfo(fileName); ;
newProcess.Start();
_startedProcesses.Push(newProcess);
}
When the application closes, you can call a method that will close all started child processes that are still running. You can use this either with the Kill method or by calling the CloseMainWindow and Close methods. CloseMainWindow/Close will perform a more graceful close (if you start Notepad and there are unsaved changes, Kill will lose them, CloseMainWindow/Close will make notepad ask if you want to save):
private void CloseStartedProcesses()
{
while (_startedProcesses.Count > 0)
{
Process process = _startedProcesses.Pop();
if (process != null && !process.HasExited)
{
process.CloseMainWindow();
process.Close();
}
}
}
The most graceful way to do this is probably to send a window message to the main from of the other process. You can get the handle of this main form simply using the Process.MainWindow.Handle property (I assume you are using the Process class, and then just use the PostMessage Win API call to send a message with a custom ID to the main window of this "child" process. Then, the message loop of the other process can easily detect this message (by overriding the WndProc method) and perform a proper shutdown accordingly. An alternative would be to send the standard WM_CLOSE method, which would mean you would just have to unload the application from the handler of the Form.Closed event, but may perhaps allow you less control (over whether to cancel the shutdown in certain situations).

Categories

Resources