I hope I can explain this clearly enough. I have my main form (A) and it opens 1 child form (B) using form.Show() and a second child form (C) using form.Show(). Now I want child form B to open a form (D) using form.ShowDialog(). When I do this, it blocks form A and form C as well. Is there a way to open a modal dialog and only have it block the form that opened it?
When a form is displayed as a modal dialog box, clicking the Close button (the button with an X at the upper-right corner of the form) causes the form to be hidden and the DialogResult property to be set to DialogResult. Cancel . Unlike non-modal forms, the Close method is not called by the .
Show() method shows a windows form in a non-modal state. ShowDialog() method shows a window in a modal state and stops execution of the calling context until a result is returned from the windows form open by the method.
ShowDialog returns a Nullable<T>Boolean value that specifies whether the activity was accepted or canceled. The return value is the value of the DialogResult property before a window closes.
When a form is shown using the ShowDialog method, it is necessary to set the form's DialogResult property to close to form. This property can be set using the enum that's also called DialogResult. To close a form, you just need to set the form's DialogResult property (to any value by DialogResult.
Using multiple GUI threads is tricky business, and I would advise against it, if this is your only motivation for doing so.
A much more suitable approach is to use Show()
instead of ShowDialog()
, and disable the owner form until the popup form returns. There are just four considerations:
When ShowDialog(owner)
is used, the popup form stays on top of its owner. The same is true when you use Show(owner)
. Alternatively, you can set the Owner
property explicitly, with the same effect.
If you set the owner form's Enabled
property to false
, the form shows a disabled state (child controls are "grayed out"), whereas when ShowDialog
is used, the owner form still gets disabled, but doesn't show a disabled state.
When you call ShowDialog
, the owner form gets disabled in Win32 code—its WS_DISABLED
style bit gets set. This causes it to lose the ability to gain the focus and to "ding" when clicked, but doesn't make it draw itself gray.
When you set a form's Enabled
property to false
, an additional flag is set (in the framework, not the underlying Win32 subsystem) that certain controls check when they draw themselves. This flag is what tells controls to draw themselves in a disabled state.
So to emulate what would happen with ShowDialog
, we should set the native WS_DISABLED
style bit directly, instead of setting the form's Enabled
property to false
. This is accomplished with a tiny bit of interop:
const int GWL_STYLE = -16; const int WS_DISABLED = 0x08000000; [DllImport("user32.dll")] static extern int GetWindowLong(IntPtr hWnd, int nIndex); [DllImport("user32.dll")] static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong); void SetNativeEnabled(bool enabled){ SetWindowLong(Handle, GWL_STYLE, GetWindowLong(Handle, GWL_STYLE) & ~WS_DISABLED | (enabled ? 0 : WS_DISABLED)); }
The ShowDialog()
call doesn't return until the dialog is dismissed. This is handy, because you can suspend the logic in your owner form until the dialog has done its business. The Show()
call, necessarily, does not behave this way. Therefore, if you're going to use Show()
instead of ShowDialog()
, you'll need to break your logic into two parts. The code that should run after the dialog is dismissed (which would include re-enabling the owner form), should be run by a Closed
event handler.
When a form is shown as a dialog, setting its DialogResult
property automatically closes it. This property gets set whenever a button with a DialogResult
property other than None
is clicked. A form shown with Show
will not automatically close like this, so we must explicitly close it when one of its dismissal buttons is clicked. Note, however, that the DialogResult
property still gets set appropriately by the button.
Implementing these four things, your code becomes something like:
class FormB : Form{ void Foo(){ SetNativeEnabled(false); // defined above FormD f = new FormD(); f.Closed += (s, e)=>{ switch(f.DialogResult){ case DialogResult.OK: // Do OK logic break; case DialogResult.Cancel: // Do Cancel logic break; } SetNativeEnabled(true); }; f.Show(this); // function Foo returns now, as soon as FormD is shown } } class FormD : Form{ public FormD(){ Button btnOK = new Button(); btnOK.DialogResult = DialogResult.OK; btnOK.Text = "OK"; btnOK.Click += (s, e)=>Close(); btnOK.Parent = this; Button btnCancel = new Button(); btnCancel.DialogResult = DialogResult.Cancel; btnCancel.Text = "Cancel"; btnCancel.Click += (s, e)=>Close(); btnCancel.Parent = this; AcceptButton = btnOK; CancelButton = btnCancel; } }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With