【问题标题】:WPF App loses focus completely on window closeWPF 应用程序完全失去对窗口关闭的关注
【发布时间】:2011-03-09 19:27:08
【问题描述】:

问题描述

如果我通过将窗口的所有者设置为父窗口来将非模态窗口设置为子窗口,然后在此子窗口中显示MessageBox,如果我关闭父窗口,父窗口将失去焦点子窗口。如果 Windows 资源管理器或其他应用程序打开,此应用程序将获得焦点,我的主窗口将被隐藏。

这似乎是一个已知问题,因为我在另一个 newsgroups 中看到了它,但我没有看到一个好的解决方案。在OnDeactivate 中将所有者设置为空不是一种选择。在将 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 或者手动强制关闭父级的焦点可能是可行的,但这在我看来非常不干净(而且还有一点如果同一个父级有更多的活动拥有的窗口,就像我在当前的应用程序中那样)。

没有人知道一个巧妙的解决方案吗?

资源

MSDN Description(请参阅下面的备注打开调用 Show() 的非模态窗口)

Same problem on MSDN forums without appropriate solution

另请参阅:Instable focus of WPF apps

【问题讨论】:

    标签: .net wpf


    【解决方案1】:

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

    如果一个窗口拥有第二个窗口,而后者又拥有第三个窗口,也会发生这种情况。 Win32 API 中可能存在 BUG,但它一直伴随着我们,所以祝你好运。

    解决方法是在孙子的 Closing 事件中手动将焦点交还给子。但在这种情况下,孙子是一个消息框,所以你不能这样做。最简单的做法是拥有消息框给父级。下一个最简单的方法是制作您自己的消息框控件。

    【讨论】:

    • 第二段准确地描述了我的问题。你有更多这方面的信息吗?
    • 不,很遗憾我没有。很久以前就遇到过写Win32代码的bug。唯一的解决方案是处理 WM_CLOSE 并将 SetFocus() 调用回窗口的所有者。在 WinForms 中,WM_CLOSE 转换为 ϞFormClosing,Form 类上有一个 Focus() 方法。 WPF 肯定有类似的东西。
    • 复制/粘贴。它看起来像事件的智能感知图标。
    【解决方案2】:

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

    在Window_Unloaded 或Window_Closing 事件中调用Me.Owner.Focus。

    但这仍然不能 100% 工作,在正确恢复焦点之前,您仍然会看到背景窗口闪烁到前景(非常短暂)。

    我找到了一个更好的方法:

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

    【讨论】:

    • 看来我得这么做了。我将把它变成一个可重用的行为,所以我所有的观点都可以很好地使用它。顺便说一句,我似乎遇到了与 OP 提到的两个不同的场景。
    • 除了 Owner.Activate() 之外,我还使用了 Owner.Focus();否则,我的键盘快捷键无法立即使用。
    【解决方案3】:

    2020 年依然有麻烦...

    Wpf 的解决方法:

        public ContactsWindow()
        {
            InitializeComponent();
    
            Closing += ContactsWindow_Closing;
        }
    
        private void ContactsWindow_Closing(object sender, System.ComponentModel.CancelEventArgs e)
        {
            Owner?.Activate();
        }
    

    它似乎对我有用,对任何新闻感兴趣

    【讨论】:

      【解决方案4】:

      也许我在这件事上已经很晚了。
      @HCL 通过将 MessageBox.show() 的 options 参数设置为 MessageBoxOptions.None,可以更轻松地解决此问题。

      干杯...

      【讨论】:

        【解决方案5】:

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

        您检查了 caliburn mvvm frameork 示例,消息框作为服务有一个很好的实现。

        【讨论】:

          【解决方案6】:

          所有权与激活哪个窗口无关。要查看这一点,请将第二个窗口的所有者更改为顶部窗口并运行程序。请注意,没有任何变化。所有权与“如果窗口最小化,其他窗口需要最小化”有关。因此,您只能使用自定义激活码。

          【讨论】:

            【解决方案7】:

            Johsua 的 .Activate(见上文)解决方案非常适用于各种窗口类型。 至少它对我有用。

            【讨论】:

              【解决方案8】:

              我遇到了类似的问题。我有一个主窗口,其余子窗口通过 Owner 属性分配给该窗口。如果主窗口有两个或多个孩子,在关闭第二个孩子后,应用程序失去焦点。我在关闭之前使用了 child.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;
                      }
              

              【讨论】:

                猜你喜欢
                • 2012-12-22
                • 1970-01-01
                • 2011-03-23
                • 1970-01-01
                • 2012-04-17
                • 2013-08-27
                • 1970-01-01
                • 2014-03-17
                • 1970-01-01
                相关资源
                最近更新 更多