WPF应用程序在关闭窗口时完全失去焦点

46

问题描述

如果我将一个非模态窗口设置为父窗口的子窗口,方法是将该窗口的Owner属性设置为父窗口,然后从此子窗口中显示一个MessageBox,如果我关闭子窗口,则父窗口将失去焦点。 如果Windows资源管理器或另一个应用程序处于打开状态,则此应用程序将获取焦点并隐藏我的主窗口。

这似乎是一个已知的问题,我在另一个论坛上看到过,但我没有看到好的解决方案。 在OnDeactivate中将所有者设置为null不是选项。 在显示MessageBox之前将所有者设置为null并在之后重置也没有帮助。 在OnClosed事件中将所有者设置为null也没有帮助。

找到的简单解决方案

如果您遇到与我描述的相同的问题,请在所有子窗口的OnClosing中放置以下代码。

void OnClosing(System.ComponentModel.CancelEventArgs e)
{
    base.OnClosing(e);
    if (null != Owner) {
        Owner.Activate();
    }
    // ....
}

该逻辑可跟随任何进一步的处理逻辑,甚至容忍打开MessageBoxes

示例代码

问题似乎比我想象的要严重得多。如果打开消息框并关闭子窗口,则以下示例将删除父窗口的焦点(将代码复制到窗口的加载事件处理程序中)。

Window firstChildWindow = new Window() {
    Title = "Floating Window", Width = 100, Height = 70
};
firstChildWindow.Owner = Window.GetWindow(this);    
Button button = new Button() { Content="MessageBox"};
button.Click += delegate { MessageBox.Show("Klicking her breaks the focus-chain."); };
firstChildWindow.Content = button;
firstChildWindow.Show();

此外,这个例子还会打破焦点链:

Window firstChildWindow = new Window() {
    Title = "Floating Window", Width = 100, Height = 70
};
firstChildWindow.Owner = Window.GetWindow(this);
firstChildWindow.Show();    
Window secondChildWindow = new Window() { Title="Second Window", Width=100, Height=70};
secondChildWindow.Content = new TextBlock() { Text="SecondWindow"};
secondChildWindow.Owner = firstChildWindow;
secondChildWindow.Show();
有人能解决这个问题吗?我想到了一个方法,使用 Dispatcher 或 DispachterTimer 触发在关闭后将焦点给予父级,或者手动在关闭时强制将焦点给予父级,但这些方法看起来都不太干净(而且如果存在更多活动的同一父窗口的 owned 窗口,就会变得更加复杂,我的当前应用程序中就是这种情况)。
没有人知道一个简洁的解决方案吗?
资源: MSDN 描述(请参见非模态窗口通过 Show() 打开的说明) MSDN 论坛上的同样问题,但没有适当的解决方案 另请参阅:WPF 应用程序的不稳定焦点
8个回答

29

这里最可能发生的情况是您有两个独立的顶级窗口,并关闭其中一个。这有时会导致焦点跳转到另一个应用程序。

如果一个窗口拥有第二个窗口,后者又拥有第三个窗口,则也会发生这种情况。这可能是Win32 API中的BUG,但它一直存在,所以祝你好运修复它。

解决方法是在子窗口关闭事件中手动将焦点返回给子窗口。但在这种情况下,子窗口是一个消息框,所以无法这样做。最简单的方法是将消息框分配给父窗口。次简单的方法是创建自己的消息框控件。


第二段描述了我的问题。你有更多相关信息吗? - HCL
很抱歉,我没有。我很久以前在编写Win32代码时遇到了这个错误。唯一的解决方案是处理WM_CLOSE并调用SetFocus()返回到窗口的所有者。在WinForms中,WM_CLOSE转换为ϞFormClosing,并且Form类上有一个Focus()方法。WPF应该也有类似的东西。 - Joshua
看起来像是事件的智能感知图标。 - Joshua

26

这是一个非常烦人的bug,最常见的解决方案是:

Window_UnloadedWindow_Closing 事件中调用 Me.Owner.Focus

但这仍然不能百分之百地解决问题,你仍然会看到背景窗口在焦点正确恢复之前闪烁到前台(非常短暂)。

我发现了一种更好的方法:

Me.Close (或 _Closing 事件) 之前调用 Me.Owner.Activate


看起来我得这么做。不过我会将其放入可重用的行为中,这样所有我的视图都可以使用它以确保良好的效果。顺便说一下,似乎我遇到了一个与OP提到的两个场景不同的问题。 - Andy
除了使用Owner.Activate()之外,我还使用了Owner.Focus();否则,我的键盘快捷键不能立即正常工作。 - scottheckel

4

2020年仍然存在的问题...

Wpf 的解决方法:

    public ContactsWindow()
    {
        InitializeComponent();

        Closing += ContactsWindow_Closing;
    }

    private void ContactsWindow_Closing(object sender, System.ComponentModel.CancelEventArgs e)
    {
        Owner?.Activate();
    }

对我来说似乎有效,如果有任何消息请告诉我


2
也许我对这个问题有些迟钝。
@HCL,通过将MessageBox.show()options参数设置为MessageBoxOptions.None可以更轻松地解决这个问题。

干杯...


0
我曾经遇到过类似的问题。我有一个主窗口,其他子窗口通过Owner属性分配给该窗口。如果主窗口有两个或更多的子窗口,在关闭第二个子窗口后,应用程序会失去焦点。我使用了一个小技巧,在关闭之前将子窗口的Owner设置为null,但是接下来我的下一个子窗口就会失去焦点。我通过获取所有子窗口并在关闭子窗口之前将焦点设置为最后一个子窗口来解决这个问题。以下是代码示例:
private void OnClosing(object sender, CancelEventArgs cancelEventArgs)
        {
            var childWindows = (sender as Window).Owner.OwnedWindows;
            if (childWindows.Count - 2 >= 0)
                childWindows[childWindows.Count - 2].Focus();
            else
            {
                (sender as Window).Owner.Focus();
            }
            (sender as Window).Owner = null;
        }

0

Johsua的.Activate(见上文)解决方案可以很好地处理各种窗口类型。至少对我来说是这样。


0

你可以编写自己的消息框,可以将其实现为一个浮动窗口,在显示时禁用整个屏幕。

你可以查看Caliburn MVVM框架示例,其中有一个很好的消息框服务实现。


0

所有权与窗口激活无关。要证明这一点,将第二个窗口的所有者更改为顶部窗口并运行程序。注意到没有任何变化。所有权与“如果一个窗口被最小化了,需要最小化哪些其他窗口”有关。因此,你必须使用自定义激活代码。


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