关闭模态窗口后,父窗口居然跑到了其他窗口的后面

关闭模态窗口后,父窗口居然跑到了其他窗口的后面

发布于 2018-02-05 05:58 更新于 2018-06-05 02:55

显示一个模态窗口,正常而普遍的操作。然而却一直有一个难缠的 BUG:当关闭模态窗口时,父窗口有时会跑到其他程序窗口的后面!

而最近读到了微软工程师写过的话之后,明白了这个 BUG 的产生缘由以及解决方法。


这是什么 BUG?

  1. 弹出一个模态窗口,然后将模态窗口的父窗口设置为自身窗口;
  2. 切换到其他程序窗口中(比如 Windows 资源管理器窗口);
  3. 切换回此模态窗口,然后关闭这个模态窗口上。

你会发现,模态窗口关闭后,父窗口并没有回到当前的顶层显示中。取而代之的,是其他程序的窗口(比如 Windows 资源管理器窗口)。 用一张图来描述这个 BUG,将是这样的:

有这两个窗口,其中右边那个是我们开发的:

我们的窗口在资源管理器上面。然后,我们弹出模态子窗口:

现在,我们操作一下资源管理器:

然后,回到模态子窗口中,把它关掉:

我们期待模态子窗口关掉后,它的父窗口会在顶层继续供我们操作,但实际上,Windows 资源管理器却成为了顶层,我们的程序“掉下去了”:

解释和解决方法

在《Windows 进化启示录》书中,微软有说到:

当销毁模态对话框时,这个对话框刚好是拥有前台焦点的窗口。现在,窗口管理器需要找到其他的窗口并把前台焦点交给这个窗口。 窗口管理器会首先试着把前台焦点交给对话框的所有者窗口,但此时这个窗口却仍然是禁止的,因此窗口管理器将跳过所有者窗口,并继续查找没有被禁止的窗口。

这很明显是 Windows 的 BUG,然而让微软感到无奈的是,经常有程序喜欢依赖于微软的 BUG 进行开发,一旦微软修复了 BUG,那些依赖于 BUG 开发的程序将变得不正常!

为解决兼容性问题的微软工程师默哀一分钟……

我曾经尝试在模态子窗口关闭后激活一下父窗口,但这样会导致窗口的层级闪烁一下(Windows 资源管理器会短暂地显示到我们的窗口之上)。

而这本书作者推荐的方法是:

  1. 重新激活所有者窗口
  2. 销毁模态对话框

于是,我试着监听模态子窗口的 Closing 事件,在其中写下主窗口的激活调用,自此 BUG 才算解决。

public ChildModalWindow()
{
    Closing += (sender, e) => Owner?.Activate();
}

将这样的解决办法封装成附加属性给所有的模态子窗口,这样设置附加属性即可解决问题。或者统一模态子窗口的窗口样式,在样式中解决这个 BUG,这样,所有使用了此窗口样式的模态子窗口也将解决问题。


参考资料

  • 《伟大的产品:Windows 进化启示录》by 微软软件工程师 Raymond Chen (2016 年 3 月第 1 版,第 112 页)

本文会经常更新,请阅读原文: https://walterlv.com/post/fix-owner-window-dropping-down-when-close-a-modal-child-window.html ,以避免陈旧错误知识的误导,同时有更好的阅读体验。

本作品采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可。欢迎转载、使用、重新发布,但务必保留文章署名 吕毅 (包含链接: https://walterlv.com ),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。如有任何疑问,请 与我联系 (walter.lv@qq.com)

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏CDA数据分析师

Excel2016四个超强的数据分析功能

摘要:三维地图、预测工作表、引用外部数据查询、数据透视表更强大的功能改进、将Excel 表格发布到Office 365 Power BI实现数据的商业智能分析…...

2815
来自专栏FreeBuf

使用Python以及C++简单绕过反爬虫机制

0x00 某些网站有反爬虫的机制 对于刚学习了几天python的我,对爬虫颇有兴趣,但是某些“想要的”网站上具有反爬虫机制,如果说使用延迟或者代理,这样的效率并...

25110
来自专栏星流全栈

React + Redux 最佳实践

2835
来自专栏项勇

[Android Studio]之设置字体大小与样式

4325
来自专栏大魏分享(微信公众号:david-share)

火力全开 | 灰度发布 | 5分钟了解一个容器典型应用场景系列

笔者在持续学习的过程中,得到了红帽淡成、王洪涛、Nico等多位大师的指点,在此表示感谢。 5分钟了解一个容器典型应用场景系列篇 关于容器解决方案的概念、架构、成...

5378
来自专栏cloudskyme

基于java的分布式爬虫

分类 分布式网络爬虫包含多个爬虫,每个爬虫需要完成的任务和单个的爬行器类似,它们从互联网上下载网页,并把网页保存在本地的磁盘,从中抽取URL并沿着这些URL的指...

4317
来自专栏ImportSource

NoSQL-Quorums-仲裁

作者简介: ? 当你权衡“一致性”或“持久性”的时候,不是一个非此即彼,非黑即白的过程。一个请求中涉及的节点越多,那么我们越有可能避免不一致。这自然就引发了一个...

3645
来自专栏iOSDevLog

Google Colab免费GPU教程

现在,你可以开发深度学习与应用谷歌Colaboratory -on的免费特斯拉K80 GPU -使用Keras,Tensorflow和PyTorch。

5035
来自专栏云计算与大数据

研发:jenkins ios应用打包

持续集成的目的,就是让产品可以快速迭代,同时还能保持高质量。它的核心措施是,代码集成到主干之前,必须通过自动化测试。只要有一个测试用例失败,就不能集成。

1583
来自专栏玉树芝兰

如何用 Python 脚本批量下载 Google 图像?

(由于微信公众号外部链接的限制,文中的部分链接可能无法正确打开。如有需要,请点击文末的“阅读原文”按钮,访问可以正常显示外链的版本。)

1662

扫码关注云+社区

领取腾讯云代金券