一个窗口使用ShowDialog打开第三个窗口后,主窗口在其他应用程序窗口后消失。

16

我注意到在一个WPF应用程序中出现了一个非常奇怪的行为。

我有一个MainWindow,使用Show()App.OnStartup中显示。说的MainWindow可以打开一个(非模态的)SubWindow,也使用Show()SubWindowOwner 设置为 MainWindow

当关闭SubWindow时,MainWindow会再次可见(很好)。

某些操作会导致SubWindow作为模态对话框打开第三个窗口,使用ShowDialog()Owner设置为SubWindow)。 当在SubWindow的寿命期内至少打开和关闭一次该模态对话框时,奇怪的事情发生了。

关闭SubWindow后,MainWindow不会出现。相反,任何随机的位于MainWindow后面的窗口会出现。 有人能解释为什么会发生这种情况,并说明如何修复吗?

无论模态对话框是使用ShowDialog()显示的普通Window,还是使用MessageBox.Show()显示的消息框,都没有任何区别。


这里是一些最小化代码,用于复现此问题。在Visual Studio中创建一个新的WPF应用程序,并将其粘贴到预生成的MainWindow.xaml.cs中

然后,按下键盘上的一个键只打开一个窗口,关闭它,行为符合预期。按两个键,关闭两个键,然后第一个窗口在Visual Studio后面(可能)。

public MainWindow()
{
    InitializeComponent();
    this.PreviewKeyDown += (sender, e) =>
    {
        if (this.Owner is MainWindow)
        {
            // we're the SubWindow

            MessageBox.Show("I am a modal dialog");

            // code below produces the exact same behavior as the message box

            //var dialog = new MainWindow();
            //dialog.Owner = this;
            //dialog.ShowDialog();
        }
        else
        {
            // we're the initial MainWindow created by App.
            var subWindow = new MainWindow();
            subWindow.Owner = this;
            subWindow.Show();
        }
    };
}

非常好的问题,提得很明确。谢谢。 - Miyamoto Musashi
6个回答

13

这是一个非常烦人的 WPF 错误,我从未找到导致它的代码缺陷,但源代码中有大量关于聚焦处理的“必须解决这个问题”的注释。只是一个权宜之计,不太理想,你可以通过在窗口关闭时显式地将焦点给予所有者来解决它。将下面的代码复制/粘贴到您的 SubWindow 类中:

    protected override void OnClosing(System.ComponentModel.CancelEventArgs e) {
        base.OnClosing(e);
        if (!e.Cancel && this.Owner != null) this.Owner.Focus();
    }

2
就此而言,大约5年前我曾在一个普通的WinForms (.NET 2)应用程序中遇到过这个bug。今天我在一个新项目(.NET 4.5)中又遇到了它。我似乎记得这个bug取决于Show()嵌套2层或更多层。看起来其他人也遇到了类似的问题(这里这里);如果我没记错,我们使用了像@Hans建议的解决方法。 - Hugh W
只是想让您知道,您的代码导致了一个无限循环。我能想象到 base.OnClosing 是一个虚函数,在您调用的方法中得到了实现,因此不断地在调用自身。 - Steztric
另外,处理“Closing”事件对我来说并不能解决问题 - 我需要处理“Closed”事件。 - Steztric
据我所知,Window.Closing事件在重写之后触发,因此可以在之后设置e.Cancel来保持窗口打开,但所有者仍然会被聚焦。 使用Closed是一种可能的解决方法,但在某些情况下无法避免问题:( - Lumo
1
在我的系统上-.Net 4.6.1,Win 7,这个程序大约有98-99%的成功率。不幸的是,1%的失败率使其不适合用于生产代码。 - Bohoo
显示剩余2条评论

1
我知道这是一个旧的帖子,但我尝试了所有这些解决方案(以及其他线程中的许多更多),但都没有起作用。我不想将我的主窗口设置为子窗口的Owner,因为我想能够最小化子窗口。
对我有用的是在主窗口中更改WindowState属性。
这是我如何在mainwindow.cs中实现的:
//create new window instance
subWindow = new SubWindow();

//event called on the mainwindow, after the subwindow has been closed. 
subWindow.Closed += (sender, args) =>
{
    subWindow = null; //clears the variable, so I can re-open the window later
    this.WindowState = WindowState.Normal; //this shows the main window!
};

//show subwindow
subWindow.Show();

0

this.Owner.Focus(); 对我不起作用,它仍然跳转到后面的窗口。我不得不花费一些时间来尝试使用TopMost,但是TopMost的问题是在关闭后需要将其返回为false。我同时使用了OnClosing和OnClosed事件。

protected override void OnClosing(System.ComponentModel.CancelEventArgs e)
{
    base.OnClosing(e);
    if (!e.Cancel && this.Owner != null) this.Owner.TopMost = true;
}

protected override void OnClosed(EventArgs e)
{
    base.OnClosed(e);
    if (this.Owner != null)
    {
        this.Owner.TopMost = false;
    }
}

0

遇到了同样的问题,只是隐藏窗口。 我看不到在这种情况下有与“关闭”相当的事件,但无论如何,这个方法可以解决:

        if (Owner != null) Owner.Focus();
        Hide();

0

我在WinForms (.NET 4.7+)中遇到了同样的问题。我的解决方法是先关闭第一个对话框(调用其Close方法),然后再显示第二个对话框。

示例

  1. 主窗体打开FirstForm
  2. FirstForm打开SecondForm并引起问题!

解决方法

在将要打开SecondFormFirstForm内调用Close方法:

// FirstForm.cs

using(var secondForm = new SecondForm())
{
    Close(); // <- this closes FirstForm instance
    secondForm.ShowDialog(owner: mainFormInstance);
}

HTH


0

虽然这是一个老问题,但我想分享一下我在C# WinForms应用程序中的解决方法,因为Owner.Focus()对我没有用。

这是Daren提供的TopMost修复方案的变体,但我发现我实际上不需要将TopMost设置为true再设置回来。只需将其设置为自身即可防止父窗口消失在其他应用程序后面。

protected override void OnClosing(CancelEventArgs e) {
    if (Owner != null)
        Owner.TopMost = Owner.TopMost;
    base.OnClosing(e);
}

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