为什么MessageBox不是最顶层的?

58

我最近发现,默认情况下MessageBox不是最顶层窗口显示,我想知道是否有任何情况下你不希望消息框在其他窗口之上显示?

当我开始在加载应用程序时显示闪屏界面时,我发现了这个问题。我的程序看起来仍在运行,但是有一个MessageBox在闪屏界面后面等待输入... 闪屏界面显示在调用消息框的线程不同的线程上,所以我想这就是为什么它没有出现在闪屏界面上方的原因;但这仍然无法解释为什么MessageBox默认情况下没有MB_TOPMOST标志。

编辑

为了更好地澄清:

最终我不得不做类似于以下内容来创建一个消息框(代码不完全正确,是根据记忆写的)

[DllImport("User32.dll")]
private int extern MessageBox(windowhandle, message, caption, flag);
public static void MessageBox(windowhandle, string message, string caption)
{
    MessageBox(windowhandle, message,caption, MB_TOPMOST);
}
5个回答

93

如果你无法获取对话框所应出现在其上的窗口的句柄或引用,则提供的解决方案将无法正常工作。然而,这可能并不总是可能或容易实现:

  • 该窗口是一个闪屏窗口,不应与您的业务逻辑紧密耦合
  • 该窗口由另一个类或库创建,与当前类不同
  • 该窗口不受您控制,即来自第三方(本地)库

在这种情况下,您可以使用来自User32.dll的Win232 MessageBox API, 但也有一个更简单的托管解决方案可供使用:

MessageBox.Show(new Form { TopMost = true }, "Hello, I'm on top!");

代码 new Form { TopMost = true } 将创建一个带有 MB_TOPMOST 属性的隐藏窗体,该属性将被消息框对话框窗口继承。因此,它将显示在您所有其他窗口的顶部。在行内使用 new Form() 不会产生任何副作用,也没有可视外观,并且它会通过垃圾回收器正常销毁。

注意:如果您不在表单中,请不要忘记名称空间,这是 System.Windows.Forms.MessageBox,而不是 System.Windows.MessageBox!(感谢user1)。


1
有趣的方法,所选择的答案是因为应用程序模态和系统模态之间的差异。我认为你的方法仍然会保持消息框应用程序模态,并且在你概述的情况下会很有用。+1 - Sayse
7
只是为了澄清,这是 System.Windows.Forms.MessageBox 而不是 System.Windows.MessageBox - JKennedy
1
这是唯一解决了我的问题的方案。谢谢Abel! - HerrimanCoder
1
谢谢,这是最好的解决方案。对于那些遇到“跨线程异常”问题的人,应该使用“new Form { TopMost = true }”而不是“this”。 - Pranay Deep
1
对于 WPF,它将是 MessageBox.Show(new Window(){ TopMost = true }, "Hello, I'm on top!"); - Paweł Iwaneczko
显示剩余6条评论

41

为了在应用程序中使MessageBox显示在最上层

代码

//Should be MessageBox.Show() below
MessageBox.Show(this, "My top most message");

默认不设置MB_TOPMOST的原因

如果将 MB_TOPMOST 设置为默认值,则 MessageBox 将在“系统模态”模式下出现,并且它将完全覆盖该表单并阻止窗口的后续操作,直到用户关闭消息框。这将导致“应用程序模态”模式无法被启用。

参考链接

  1. MSDN论坛 - 如何将MessageBox显示为最顶层窗口
  2. Stack Overflow - 当应用程序最小化到托盘时如何前置C# MessageBox

1
这个回答看起来很好!但是我认为消息框无论如何都会阻塞窗口吧? - Sayse
答案中提到的msdn论坛中,我发现有类似于“系统模态”和“应用程序模态”的东西。也许以后我会查找更多细节... - Harsh Baid
只有模态消息框会阻止其所在的窗体,而“常规”消息框即使在您退出生成它的程序后仍然可以存在。 - Tory
1
我相信 MSDN 页面是我最终解决方案的灵感来源,我认为关于系统模态与应用程序模态的观点正是我所寻找的,谢谢 :D(很快会接受) - Sayse
另外需要注意的是,想象一下如果MessageBox默认为最顶层会怎样:你正在应用程序A中工作,而应用程序B在后台运行某些操作。当你在工作时,应用程序B弹出一个MessageBox,由于它是最顶层的,所以出现在应用程序A的上方。如果没有任何上下文,最好的情况是你可能会感到困惑,最坏的情况是你可能会将消息解释为应用程序A的上下文,并单击错误选项,在应用程序B中造成一些意外的操作。 - Devin Goble

6

在显示MessageBox时,将其所有者作为第一个参数提供。例如,当从Form实例调用时,请调用:

MessageBox.Show(this, "Message");

将拥有它的窗口作为第一个参数提供参考。

消息框(以及一般的模态窗体)不会出现在应用程序所有窗口的顶部。它们只会出现在它们的所有者上方。如果您想要您的消息框(或其他模态窗体)在启动屏幕的顶部,将它们的所有者设置为启动屏幕实例即可。


我不理解你的回答。这如何确保消息框始终在所有其他窗口之上?或者这有什么作用? - Sayse
它不会。它将始终位于其所有者之上。如果您希望它显示在闪屏界面之上,请将其所有者设置为闪屏界面实例。 - Sina Iravanian

3
上面给出的答案显然是正确的,除了需要在新窗体对象上调用System.iDisposable.Dispose方法。
MessageBoxButtons buttons = MessageBoxButtons.YesNo;
MessageBoxIcon icon = MessageBoxIcon.Error;
string message = Resources.ResourceManager.GetString("MESSAGE");
string caption = Resources.ResourceManager.GetString("TITLE");
DialogResult result;
Form form;
using (form = new Form())
{
    form.TopMost = true;
    result = MessageBox.Show(form, caption, message, buttons, icon);
}
if (result == DialogResult.Yes)
{
    // do something with the result
}

更多内容请参见:

Top-Most-MessageBox示例


3

我尝试贴出一份更完整的代码片段,它绝对可以工作

    [CLSCompliant(false)]
    [DllImport("user32.dll", EntryPoint = "MessageBox")]
    public static extern int MessageBoxUser32(int hWnd, String text, String caption, uint type);

    const uint MB_TOPMOST = 0x00040000;
    const uint MB_OK  = 0x00000000;
    const uint MB_OKCANCEL = 0x00000001;
    const uint MB_ABORTRETRYIGNORE = 0x00000002;
    const uint MB_YESNOCANCEL = 0x00000003;
    const uint MB_YESNO = 0x00000004;
    const uint MB_RETRYCANCEL = 0x00000005;

     public static void ShowMessageBox(string message, bool topMost = true
        , string title = null, MessageBoxButtons buttons = MessageBoxButtons.OK)
    {
        if(topMost)
        {
            uint mbv = MB_TOPMOST;

            if (buttons == MessageBoxButtons.OK)
                mbv |= MB_OK;
            if (buttons == MessageBoxButtons.OKCancel)
                mbv |= MB_OKCANCEL;
            if (buttons == MessageBoxButtons.AbortRetryIgnore)
                mbv |= MB_ABORTRETRYIGNORE;
            if (buttons == MessageBoxButtons.YesNoCancel)
                mbv |= MB_YESNOCANCEL;
            if (buttons == MessageBoxButtons.YesNo)
                mbv |= MB_YESNO;
            if (buttons == MessageBoxButtons.RetryCancel)
                mbv |= MB_RETRYCANCEL;

            MessageBoxUser32(0, message, title == null ? string.Empty : title, MB_TOPMOST);
        }
        else
        {
            MessageBox.Show(message, title == null ? string.Empty : title, buttons);
        }
    }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接