Why does FormClosing fire twice when calling Hide() in modal dialog? - c#

We created a new form that we're showing via ShowDialog and added a "Cancel" button to it. Here's how we're opening the form from its parent:
// _modalForm is a class-level instance of our ModalForm class
var result = _modalForm.ShowDialog(this);
MessageBox.Show(result.ToString());
Here's the cancel button's Click event handler in ModalForm.
private void btnCancel_Click(object sender, EventArgs e)
{
Close();
}
In our FormClosing event, we have this code (based on this answer).
private void ModalForm_FormClosing(object sender, FormClosingEventArgs e)
{
e.Cancel = true;
Hide();
_parentForm.RefreshData();
}
Surprisingly, when we click the "Cancel" button (or use the "X" button at the top of the form), the FormClosing event is raised twice. Both times the CloseReason is UserClosing.
I double checked to make sure InitializeComponent isn't call twice and that we only subscribe to the event once. btnCancel is not set at the CancelButton property for the form. It also doesn't have DialogResult set in the designer. When I check the return value of ShowDialog though, it is set to DialogResult.Cancel.
Changing btnCancel_Click to just be DialogResult = DialogResult.Cancel instead of Close() and doing nothing except _parentForm.Refresh() in the FormClosing event fixes the issue of the event getting raised twice.
Does anyone know why in this particular scenario the FormClosing event gets raised twice?

That's because hiding a modal form will cause it to close with DialogResult.Cancel as dialog result. So if you call this.Hide() in FormClosing event, the event will be raised again.
Imagine if it didn't close the form, your application had been blocked by a hidden modal form!
Note: The answer describes about the reason of raising the event twice. But as described here and others mentioned, For modal forms (which you showed using ShowDialog), the Dispose method will not be called and the form exists after closing and you can use its properties to get some data or you can show it again. So you don't need to call hide method.
For more information take a look at: Do I need to Dispose a Form after the Form got Closed?

The workaround you mention is unnecessary, because modal dialogs are not disposed when closed. They are designed to keep their data after being closed (since it is required by the caller of the modal dialog), and to be reused as needed.
Just let it close properly. It causes no harm to a modal dialog :)
Note that this also means that unlike normal forms, you must dispose of modal dialogs manually if they are not persistent. In your scenario, this most likely means you'd want to dispose of the dialog when the parent form is disposed (this happens automatically if you added the dialog as a component, but needs to be done manually if you create it yourself).

Related

How to force close button to terminate all childforms?

I have program that opens subwindows inside of it (mdi.parent). I have made component that is in one window under it, however, i want that that window never actually disposed after its created because i want to keep only one instance of it.
This can be made with code:
// This prevents this window disposing from window close button, we want always show one and only
// one instance of this window.
FormClosing += (o, e) =>
{
Hide();
e.Cancel = true;
};
However, after this there is problem, closing program requires pressing close button press twice. First press closes subwindow and second terminates program. How this can be get around?
I am working with Winforms.
As Habib said, you can call Application.Exit, but:
The Form.Closed and Form.Closing events are not raised when the
Application.Exit method is called to exit your application
If this is important to you, you can do something like this (MDI parent code):
private Boolean terminating;
protected override void OnClosing(CancelEventArgs e)
{
if (!terminating)
{
terminating = true;
Close();
}
base.OnClosing(e);
}
Call Application.Exit() in the form close event.
Application.Exit - MSDN
Informs all message pumps that they must terminate, and then closes
all application windows after the messages have been processed.
The code inside of your FormClosing event handler method is a bit too terse. It does its job of preventing the user from closing the form, but as you've also noticed, it prevents you from closing the form programmatically as well.
This is easily solved by testing the value of the CloseReason property of the FormClosingEventArgs that are passed in each time the event is raised.
These will tell you the reason why the form is attempting to close. If the value is CloseReason.UserClosing, then you want to set e.Cancel to true and hide the form. If the value is something else, then you want to allow the form to continue closing.
// This prevents this window disposing when its close button is clicked by the
// user; we want always show one and only one instance of this window.
// But we still want to be able to close the form programmatically.
FormClosing += (o, e) =>
{
if (e.CloseReason == CloseReason.UserClosing)
{
Hide();
e.Cancel = true;
}
};
Use This
Form[] ch = this.MdiChildren;
foreach (Form chfrm in ch)
chfrm.Close();
You can use Application.Exit if there is no processing happening when the application is closed. Otherwise, you can check Application.OpenForms collection in MDI parent's closing event and close all the other forms that are open.

How to keep the focus back to form when clicking on OK button in windows forms?

I have a windows form with multiple controls and OK and Cancel buttons. In the Click event handler of OK button, it checks to see if there are errors on the form. I need the form to stay open if there are errors. But the code below is not working. If there are errors, it is closing the form and returning to the caller. And I have the below two lines to invoke and show the form.
PtForm paymentForm = new PtForm();
ptForm.ShowDialog();
private void btnOk_Click(object sender, EventArgs e)
{
this.ValidateChildren(ValidationConstraints.Visible);
string ErrorString = GetValidationErrors();
MessageBox.Show(ErrorString, "Errors");
if (!string.IsNullOrEmpty(ErrorString))
{
return;
}
//Processing
}
Thanks for any help.
There's nothing in this code that will close the form. Therefore, the culprit must be outside this code.
Did you set your OK button's DialogResult property to DialogResult.OK? That would explain why the form is closing. AFAIK, if you set DialogResult on the button, that's what happens -- there's no way to veto it in code.
So in that case, you would need to go back to the designer and set the button's DialogResult back to None. Then at the end of your btnOk_Click method, once you've validated all the input and decided that it's safe to close the dialog, add a line that sets your Form's DialogResult property to OK.
Remove DialogResult property of a button-i.e. set it to None.

How to prevent closing of a HIDDEN form in C#

I want to prevent closing of a form in some cases. I know the usage of OnFormClosing,
but when the form is hidden (Visible==false), the OnFormClosing method is not called.
Is there a way to intercept form closing in this case?
Edit (some more details):
The form is a child in a MdiParent, should stay invisible in the background and wait for calls from another thread (by Invoke).
The MdiParent closes all child windows when the user "disconnects", in this case the above form should stay open, but invisible and still waiting for calls.
When the MidParent itself is closed, all forms should close.
Edit2 (no solution?):
It seems that there is no solution to this. My workaround now is to exclude my not-to-be-closed form in the MdiParent-code, that closes all other forms.
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
// This will cancel the event
e.Cancel = true;
}
Regardless of the reason, this will effectively stop a form from closing.

How to prevent MDI main form closing from MDI Child

I have a MDI main form. On it I host a form, and I want it to show a message box before it closes (asking the user whether to save changes).
So far so good, however I have discovered that closing the MDI main form does not raise a MDI child FormClosing event. I figured I will just call MdiChild.Close() in the MDI main's FormClosing event (which does get raised). This seams to work, however it does causes a problem...
In the messagebox that I show, I offer the user to save changes, not to save changes and cancel closing. Normally this works fine, however I can't seem to find a way to cancel MDI main's FormClosing event. Is there a elegant way of doing this?
EDIT: I solved this by throwing an exception (when user decides to cancel the closing procedure) which is caught in MDI main's FormClosing event. In this way I know when I have to cancel the MDI main's FormClosing event and this seams to work fine ... However I just can't believe that this "hax" is the only way of doing this. Surely there is a better way?
I figure that you cancel the close on the childform when the user votes to cancel the close?
In that case I'd go with this Main form close
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
if (e.CloseReason == CloseReason.UserClosing)
{
foreach (Form frm in this.MdiChildren)
{
frm.Close();
}
}
e.Cancel = (this.MdiChildren.Length > 0);
}
I'd suggest that instead of calling Close on the childforms you could create a method like ReadyToClose in the child forms and then the main form loops through and calls that for each of the child forms and it'll ask the question to the user and do the saving if needed and finally it'll return true if ok to continue.
And if all of the child forms "vote" for a close, then you close it all down, otherwise you close nothing.
When closing the MDI main form, all child Close events are called first so, when calling frm.Close() on the foreach loop, Close events for the child are called again (I don't know why if they should be already closed).
ho1 suggestion worked pretty well for me. Here is my modified foreach loop (ClosableMDIChildForm is an interface which contains the IsOkToClose() method):
foreach (ClosableMDIChildForm cmdif in this.MdiChildren)
{
if (!cmdif.IsOkToClose())
{
e.Cancel = true;
((Form)mdifc).Focus();
break;
}
}
Obviously, this.MdiChildren forms must implement ClosableMDIChildForm interface. The logic to decide if it's OK to close the window goes in the implementation of IsOkToClose().

How to dispose of variables in windows close button

I have a windows forms application, where I have declared some static variables.
On the click of exit button, I have disposed of some datatable which i have declared static.
Many a times the user instead of clicking the exit button, will just exit the windows application by clicking the X button on the left corner top.
What should be done to ensure that even if the user clicks the X button, everything is disposed of properly.
Thanks
Regards
Hema
This question has some good descriptions of events that you can hook into to detect when a application is exiting.
Does Application.ApplicationExit event work to be notified of exit in non-Winforms apps?
Just add a delegate function to the Closing event of the form.
this.Closing += this.MyForm_Closing;
You can also use the Closed event of the form if you'd prefer it gets called after the form is closed.
You can add an event handler to dispose your variables when the form is closing.
private: System::Void myDialog_FormClosing(System::Object^ sender, System::Windows::Forms::FormClosingEventArgs^ e) {
// Dispose your static variables here
}

Categories