MessageBox modal for one form

I want to render MessageBox

that is modal for only one form and allows other top-level windows in the thread to remain active. WinForms disables all windows of the current thread, regardless of whether a custom modal form is displayed or the class is used MessageBox

.

I've tried several different approaches to solving the problem, including re-enabling any other top-level windows (using a Win32 function EnableWindow

) and p / calling a built-in function MessageBox

. Both of these approaches work, but leave keyboard shortcuts inoperable, including tab to navigate between controls, escape to cancel an open menu, and most importantly, keyboard shortcuts.

I can make the shortcut keys to the menu by setting the keyboard hook when I show MessageBox

and then directly invoke ProcessCmdKey

on the active form, but the other keyboard shortcuts still don't work. I guess I could push and add more hacks to make these other things work, but I was wondering if there is a better way.

Note , this is not about non-blocking , but about not disabling all windows of the current thread. However, it happens that the solution may be similar.

+3


source to share


2 answers


The main problem you are having is MessageBox.Show () is pushing its own message loop to make itself modal. This message loop is built into Windows and has no complete knowledge of what a Winforms message loop looks like. So nothing special that Winforms does in its message loop just happens when you use the MessageBox. Keyboard control: mnemonic detection, implementation of navigation and callers such as ProcessCmdKey () so that the form can implement its own keyboard shortcuts. This is usually not a problem as it should be modal and ignore user input.

The only practical way to liven up this is to display a message box on your thread. This is formally allowed in winapi, the owner of the window can be a window owned by another thread. But it's a rule that Microsoft hasn't implemented by adding code in .NET 2.0 that detects streaming errors. Working on this code requires IWin32Window to be the owner of the message box, which is also not a control.

Add a new class to your project and paste this code:

using System;
using System.Threading;
using System.Windows.Forms;

public class NonModalMessageBox : IWin32Window {
    public NonModalMessageBox(Form owner, Action<IWin32Window> display) {
        this.handle = owner.Handle;
        var t = new Thread(new ThreadStart(() => display(this)));
        t.SetApartmentState(ApartmentState.STA);
        t.Start();
    }
    public IntPtr Handle {
        get { return handle; }
    }
    private IntPtr handle;
}

      



And use it like this:

        new NonModalMessageBox(this, (owner) => {
            MessageBox.Show(owner, "Testing", "Non-modal");
        });

      

If it is a form object that should be disabled while the message box is displayed. There is little I can do to make you feel better about this hack, if the FUD is too overwhelming, you really need to re-implement the MessageBox with your own Form class so that you can use Show () instead of ShowDialog (). It's done.

+2


source


You can pass a link to the form, set the Enabled property to false when you open the dialog, and return it to true when the dialog closes.



0


source







All Articles