如何将WPF MessageBox的所有者设置为桌面窗口,因为启动屏幕关闭了MessageBox。

13

我正在使用WPF中的SplashScreen功能,通过将位图的Build Action设置为Splashscreen来实现。在启动屏幕后,正在检查许可信息,如果失败则显示一个MessageBox

根据这个反馈,这是因为MessageBox.Owner是启动屏幕,即使打开另一个窗口,即使是MessageBox,启动屏幕窗口也会关闭,然后依次关闭MessageBox,因此用户永远看不到MessageBox。

因此解决方法是将MessageBox.Owner设置为另一个窗口,但这意味着我必须实例化另一个可能不需要的窗口。

是否可以将MessageBox.Owner设置为桌面窗口? 如何操作,因为我所想到的唯一其他功能是GetDesktopWindow() api函数,但它返回一个窗口句柄,而 MessageBox.Owner 是一个 WPF 窗口。

8个回答

11

由于将桌面窗口作为模态对话框的父级并不是一个好主意,正如@Nir在他的答案中指出的那样,这里有三个其他的解决方法:

1)使用隐藏窗口。创建一个微小的非模态窗口作为您的MessageBox或其它模态对话框的父窗口。这种方法在这里描述:

http://social.msdn.microsoft.com/Forums/en-US/wpf/thread/116bcd83-93bf-42f3-9bfe-da9e7de37546/

2)创建非模态消息窗口。将启动模式更改为显式关闭,并使用非模态窗口显示您的消息。这种方法在StackOverflow问题的答案中描述:

MessageBox with exception details immediately disappears if use splash screen in WPF 4.0

3)调用MessageBox两次。显然,该问题仅影响第一个显示的模态对话框。因此,如果您不介意第一个对话框的闪烁,可以简单地调用两次模态对话框。

https://connect.microsoft.com/VisualStudio/feedback/details/600197/wpf-splash-screen-dismisses-dialog-box

个人而言,我不喜欢这些解决方法。唯一的选择是避免内置的SplashScreen功能,从头开始制作您自己的窗口。如果您想进一步研究这条路,请参考以下链接:

http://social.msdn.microsoft.com/Forums/en-US/wpf/thread/8dd49fd0-9cc9-43c6-b285-6f119ab8a32e/

最终,如果您和我一样被这个问题所困扰,您可以在此处投票要求Microsoft修复此漏洞:

http://connect.microsoft.com/VisualStudio/feedback/details/600197/wpf-splash-screen-dismisses-dialog-box


9
我自己想出了这个解决方案,所以可能有些问题,但它似乎完美地运作:
Window temp = new Window() { Visibility=Visibility.Hidden };
temp.Show();
MessageBox.Show(temp, "An error occurred before the application could start.\n\nTechnical Details: " + ex.Message, "Fatal Error", MessageBoxButton.OK, MessageBoxImage.Stop);
App.Current.Shutdown(1);

2
不错且简单的解决方法。然而,Visibility=Visibility.Hidden并不是必需的,因为你无论如何都要显示窗口。相反,为了避免背景窗口的闪屏显示,我使用了:new Window { WindowState = WindowState.Minimized, ShowInTaskbar = false } - splintor
new Window { AllowsTransparency = true,ShowInTaskbar=false,WindowStyle=WindowStyle.None, Background=Brushes.Transparent} 可以使窗口完全隐藏,而设置可见性或窗口状态会导致在调用 Show() 时窗口显示几毫秒。 - kiran

3

现在的问题是,你如何将MessageBox的所有者设置为桌面窗口? - adriaanp

1

你能发一些代码吗?我刚试着将这段代码添加到一个新的 WPF 应用程序的 App.xaml.cs 文件中:

protected override void OnStartup(StartupEventArgs e)
{
    if (MessageBox.Show("Start app?", "Confirm Start", 
        MessageBoxButton.YesNo) == MessageBoxResult.No)
    {
        this.Shutdown();
        return;
    }

    this.StartupUri = new Uri("Window1.xaml", UriKind.Relative);
    base.OnStartup(e);
}

...并且它按预期工作(“确认启动”提示保持打开状态,直到我回复,如果我点击“否”,应用程序将关闭)。


Matt,我也像你一样测试了它,并意识到它确实有效,因为我正在使用启动画面,它关闭了MessageBox,如果没有启动画面,它就不会关闭。 - adriaanp
所以现在你需要决定是将问题标记为“不再相关”,还是(如@adriaanp在评论中建议的那样)将其改写为不同的问题。 - Matt Hamilton

0

这个问题仍然存在,我最近也遇到了。对我来说,解决方案是在出现任何问题时立即关闭启动画面:

        SplashScreen splash = new(Assembly.GetAssembly(typeof(GuiApp))!, "Resources/img_my.png");
        splash.Show(false);

        using ServiceProvider? services = initializeApp();

        if (services == null)
        {
            splash.Close(TimeSpan.Zero);
            return -3;
        }

之后,消息框正常显示。


0

-1

这与OP的情况没有直接关系,但对于其他在某些特殊情况下遇到MessageBox被隐藏在其他窗口后面的人可能会有用。

正如@dthrasher所提到的,其中一个解决方案是使用一个隐藏的虚拟窗口。但有时即使这样还不够。我曾经遇到一种情况,解决方案是不仅使用一个隐藏的虚拟窗口,而且每当我使用它与MessageBox时都要打开其TopMost属性。

     _formKludge.TopMost = true;

     MessageBox.Show(_formKludge, "Nice informative message.", "Super-duper Program",
                     MessageBoxButtons.OK, MessageBoxIcon.Error);

     _formKludge.TopMost = false;

-1

这对我帮助很大......给了我新的想法,但是我在这里看到的示例代码需要进行一些修改。

这里有一个简单的WPF示例,需要进行修改,现在它可以工作了。

在按钮单击事件中,粘贴以下代码:

if (System.Windows.Forms.MessageBox.Show("你确定吗?", "删除", System.Windows.Forms.MessageBoxButtons.YesNo, System.Windows.Forms.MessageBoxIcon.Question) == System.Windows.Forms.DialogResult.Yes) { this.Close(); } else { MessageBox.Show("为什么不删除呢?"); }


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