Winforms: Close modal dialog when clicking outside

2020-02-10 12:26发布

问题:

I have an open modal dialog (Windows Forms). I want, that the dialog is closed when clicking outside the dialog (on the parent form). How can I do that?

回答1:

You should change it to a non-modal dialog (open it with Show(..)) and then use the Deactivate event and close it.



回答2:

That cannot work, you must use Show() to get the Deactivate event to fire.

A dialog disables all of the other windows to make itself modal. So there's nothing that can be clicked on outside of the dialog window with the parent maximized. Accordingly, the Deactivate event isn't going to fire. When you use the Show(owner) method instead, this side-effect of ShowDialog() no longer gets in the way and Deactivate is fine. Use the FormClosing/Closed event to do what you would do after a ShowDialog() call.

The way this is normally done, with a context menu strip or menu dropdown for example, is by capturing the mouse, Control.Capture property. So that you can detect mouse events even outside of the window. But that can't work reliably with forms, the controls inside the form will use the Capture property for their own use. So stick with Show and Deactivate.



回答3:

There are reasons (e.g. framework workarounds etc.) which force one to use a modal dialog. You can emulate the Deactivate event by overriding the form method WndProc like this:

protected override void WndProc(ref Message m) {
    const UInt32 WM_NCACTIVATE = 0x0086;

    if (m.Msg == WM_NCACTIVATE && m.WParam.ToInt32() == 0) {
        handleDeactivate();
    } else {
        base.WndProc(ref m);
   }
}

The WM_NCACTIVATE message is send to tell the form to indicate whether it is active (WParam == 1) or inactive (WParam == 0). If you click somewhere on the parent form your modal dialog begins to "blink with activity" telling you, that it is more important right now. This is achieved by sending the previously mentioned Messages a few times (deactivate -> activate -> deactivate -> ... -> activate).

But be careful, if the handleDeactivate() method is not terminating your form, it will be called every time the deactivate messages come. You have to handle that yourself.

Links:
MSDN Forum
MSDN Documentation



回答4:

Use .Show() instead of .ShowDialog().

Handle Deactivate event in child form.

private void frmAdvancedSearch_Deactivate(object sender, EventArgs e)
    {
        this.Close();
    }

This would close the child form when user clicks outside the child.

If you are using .ShowDialog() for doing something after the child closed, use .show() and override the 'formclosing' event of child in the parent. So it would hit when the child form closes and do your stuff there.

advancedSearch.FormClosing += AdvancedSearch_FormClosing;
advancedSearch.ShowDialog();

private void AdvancedSearch_FormClosing(object sender, FormClosingEventArgs e)
    {
        var advanceSearch = sender as frmAdvancedSearch;
        if (advanceSearch.SelectedItem != null)
        {
            myColumnComboBox.Text = advanceSearch.SelectedItem.Name;
        }
    }

Hope this helps.



回答5:

hi I think u can use the mouse down event or keydown event in the event find the location fo the mouse if that location is in minus values that means u have selected the outer area of the windows and then close that windows hope this help.