Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible to use ShowDialog without blocking all forms?

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?

like image 842
Jon Tackabury Avatar asked Jan 09 '09 15:01

Jon Tackabury


People also ask

What happens after a form that has been displayed with the ShowDialog method is closed by the user?

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 .

How does the show method differ from the ShowDialog method?

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.

What does ShowDialog return?

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.

How do I close a ShowDialog form?

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.


1 Answers

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:

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

  2. 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)); } 
  3. 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.

  4. 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;     } } 
like image 94
P Daddy Avatar answered Oct 14 '22 13:10

P Daddy