3

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?

Community
  • 1
  • 1
Jeff B
  • 8,572
  • 17
  • 61
  • 140
  • 2
    You are helping too much. A dialog already hides itself when you close it so you don't have to call Hide() and don't need e.Cancel = true. What you *really* want to accomplish is preventing it from getting disposed. No help needed, it already does. You can simply call Show() again to make it visible again. Conversely, any dialog that *does* need to be dismissed completely requires an explicit Dispose() call. Always most easily done with a *using* statement. – Hans Passant Feb 16 '17 at 14:46
  • @HansPassant Good catch. The code above was a sample app to reproduce the issue. The actual application experiencing the issue doesn't use an `using` block and instead keeps a reference to the form in a class-level variable in the parent form. I've modified my question to remove the `using` block. – Jeff B Feb 16 '17 at 14:48

2 Answers2

3

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?

Community
  • 1
  • 1
Reza Aghaei
  • 120,393
  • 18
  • 203
  • 398
  • 1
    Also take a look at [Do I need to Dispose a Form after the Form got Closed?](http://stackoverflow.com/a/39501121/3110834) – Reza Aghaei Feb 16 '17 at 14:58
  • 1
    The answer describes about the reason of raising the event twice. But as described in above link 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. – Reza Aghaei Feb 16 '17 at 15:29
2

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

Luaan
  • 62,244
  • 7
  • 97
  • 116