做 Windows 桌面应用开发的小伙伴们对“模态窗口”(Modal Dialog)一定不陌生。如果你希望在模态窗口之上做更多的事情,或者自己实现一套模态窗口类似的机制,那么你可能需要了解模态窗口的本质。

本文不会太深,只是从模态窗口一词出发,抵达大家都熟知的一些知识为止。


本文内容

  • 开发中的模态窗口
  • 模态窗口的三个特点
  • 实现模态窗口
  • API 调用
    • 禁用主窗口
    • 阻塞代码等待操作完成
    • 进行 UI 强提醒

开发中的模态窗口

在各种系统、语言和框架中,只要有用户可以看见的界面,都存在模态窗口的概念。从交互层面来说,它的形式是在保留主界面作为环境来显示的情况下,打开一个新的界面,用户只能在新的界面上操作,完成或取消后才能返回主界面。从作用上来说,通常是要求用户必须提供某些信息后才能继续操作,或者单纯只是为了广告。

模态窗口的三个特点

如果你希望自己搞一套模态窗口出来,那么只需要满足这三点即可。你可以随便加戏但那都无关紧要。

  1. 保留主界面显示的同时,禁用主界面的用户交互;
  2. 显示子界面,主界面在子界面操作完成后返回;
  3. 当用户试图跳过子界面的交互的时候进行强提醒。

拿 Windows 系统中的模态对话框为例子,大概就像下面这两张图片这样:

有一个小的子界面盖住了主界面,要求用户必须进行选择。Windows 系统设置因为让背景变暗了,所以用户肯定会看得到需要进行的交互;而任务管理器没有让主界面变暗,所以用户在操作子界面的时候,模态窗口的边框和标题栏闪烁以提醒用户注意。

实现模态窗口

对于 Windows 操作系统来说,模态窗口并不是一个单一的概念,你并不能仅通过一个 API 调用就完成显示模态窗口,你需要在不同的时机调用不同的 API 来完成一个模态窗口。如果要完整实现一个自己的模态窗口,则需要编写实现以上三个特点的代码。

当然,你可能会发现实际上你显示一个模态窗口仅仅一句话调用就够了,那是因为你所用的应用程序框架帮你完成了模态窗口的一系列机制。

关于 WPF 框架是如何实现模态窗口的,可以阅读:直击本质:WPF 框架是如何实现模态窗口的

关于如何自己实现一个跨越线程/进程边界的模态窗口,可以阅读:实现 Windows 系统上跨进程/跨线程的模态窗口

如果你希望定制以上第三个特点中强提醒的动画效果,可以阅读:WPF window 子窗口反馈效果(抖动/阴影渐变) - 唐宋元明清2188 - 博客园。

API 调用

为了在 Windows 上实现模态窗口,需要一些 Win32 API 调用(当然,框架够用的话直接用框架就好)。

禁用主窗口

我们需要使用到 BOOL EnableWindow(HWND hWnd, BOOL bEnable); 来启用与禁用某个窗口。

EnableWindow(hWnd, false);
try
{// 模态显示一个窗口。
}
finally
{EnableWindow(hWnd, true);
}
[DllImport("user32")]
private static extern bool EnableWindow(IntPtr hwnd, bool bEnable);

阻塞代码等待操作完成

因为 async/await 的出现,阻塞其实可以使用 await 来实现。虽然这不是真正的阻塞,但可以真实反应出“异步”这个过程,也就是虽然这里在等待,但实际上依然能够继续在同一个线程响应用户的操作。

UWP 中的新 API 当然已经都是使用 async/await 来实现模态等待了,不过 WPF/Windows Forms 比较早,只能使用 Dispatcher 线程模型来实现模态等待。

于是我们可以考虑直接使用现成的 Dispatcher 线程模型来完成等待,方法是调用下面两个当中的任何一个:

  • Window.ShowDialog 也就是直接使用窗口原生的模态
  • Dispatcher.PushFrame 新开一个消息循环以阻塞当前代码的同时继续响应 UI 交互

上面 Window.ShowDialog 的本质也是在调用 Dispatcher.PushFrame,详见:

  • 直击本质:WPF 框架是如何实现模态窗口的

关于 PushFrame 新开消息循环阻塞的原理可以参考:

  • 深入了解 WPF Dispatcher 的工作原理(PushFrame 部分) - walterlv

当然,还有其他可以新开消息循环的方法。

进行 UI 强提醒

由于我们一开始禁用了主窗口,所以如果用户试图操作主窗口是不会有效果的。然而如果用户不知道当前显示了一个模态窗口需要操作,那么给出提醒也是必要的。

简单的在 UI 上的提醒是最简单的了,比如:

  • 将主界面变暗(UWP 应用,Web 应用喜欢这么做)
  • 将主界面变模糊(iOS 应用喜欢这么做)
  • 在模态窗口上增加一个很厚重的阴影(Android 应用喜欢这么做)

然而 Windows 和 Mac OS 这些古老的系统由于兼容性负担不能随便那么改,于是需要有其他的提醒方式。

Windows 采用的方式是让标题栏闪烁,让阴影闪烁。

而这些特效的处理,来自于子窗口需要处理一些特定的消息 WM_SETCURSOR

详见:WPF window 子窗口反馈效果(抖动/阴影渐变) - 唐宋元明清2188 - 博客园

通常你不需要手工处理这些消息,但是如果你完全定制了窗口样式,则可能需要自行做一个这样的模态窗口提醒效果。


我的博客会首发于 https://blog.walterlv.com/,而 CSDN 会从其中精选发布,但是一旦发布了就很少更新。

如果在博客看到有任何不懂的内容,欢迎交流。我搭建了 dotnet 职业技术学院 欢迎大家加入。

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

什么是模态窗口?本文带你了解模态窗口的本质相关推荐

  1. 微信小程序之自定义模态弹窗(带动画)实例

    代码地址如下: http://www.demodashi.com/demo/13991.html 一.前期准备工作 软件环境:微信开发者工具 官方下载地址:https://mp.weixin.qq.c ...

  2. 浮动窗口代码(带关闭按钮+全屏漂浮)

    <html> <head> <meta http-equiv="Content-Type" content="text/html; char ...

  3. 六元均匀直线阵的各元间距为_小间距led显示屏的封装方式有哪些?本文带你了解!...

    小间距LED显示屏产品(一般市场定义为点间距不大于2.5mm)因稳定性.可靠性.耐久性及易维护性开启了各类指挥调度中心的新时代,并不断满足其他中高端应用场景精细化.个性化的产品需求.而对于这些LED小 ...

  4. WPF 让窗口激活作为前台最上层窗口的方法

    在 WPF 中,如果想要使用代码控制,让某个窗口作为当前用户的输入的逻辑焦点的窗口,也就是在当前用户活动的窗口的最上层窗口,默认使用 Activate 方法,通过这个方法在大部分设备都可以做到激活窗口 ...

  5. html弹窗赋值给查询框,bootstrap模态框动态赋值, ajax异步请求数据后给id为queryInfo的模态框赋值并弹出模态框(JS)...

    /查询单个 function query(id) { $.ajax({ url : "/small/productServlet", async : true, type : &q ...

  6. python PyQt5中文教程☞【第二节】PyQt5基本功能(创建窗口、应用程序图标、显示提示语、通过按钮关闭窗口、消息框(关闭窗口确认框)、窗口显示在屏幕中间【居中显示】)

    引用文章:http://code.py40.com/pyqt5/ 文章目录 简单的例子:创建一个小窗口 应用程序的图标 显示提示语 通过按钮关闭窗口 消息框(关闭窗口确认框) 窗口显示在屏幕的中间[居 ...

  7. pyqt 子窗口控制主窗口绘图_实战PyQt5: 005-主窗口QMainWindow

    QMainWindow简介 在桌面应用中,一个应用软件通常都会包含一个主窗口,主窗口是承载所有控件的窗体, 在PyQt5中常用的主窗体有两种QMainWindow和QDialog,他们也都继承自QWi ...

  8. Android窗口管理服务WindowManagerService切换Activity窗口(App Transition)的过程分析

    文章转载至CSDN社区罗升阳的安卓之旅,原文地址:http://blog.csdn.net/luoshengyang/article/details/8596449 在Android系统中,同一时刻只 ...

  9. 3点 刚体运动 opencv_模态法动力学分析中的刚体模态

    01 - 概述 在对汽车结构进行动力学有限元分析时,无论是瞬态问题还是频响问题,都经常使用模态叠加法. 模态叠加法动力学分析是常规模态分析的自然扩展,它利用结构振型来缩减问题求解规模,从而使数值求解更 ...

  10. qt如何把父窗口的变量传给子窗口_父窗口和iframe子窗口之间相互传递参数和调用函数或方法...

    1.父窗口向子窗口传递参数: 可以在url中添加参数:2.html?a=1&b=2&c=3 然后在子页面上可用js解析,提供一个函数: function getQueryStr(sAr ...

最新文章

  1. prometheus--初见
  2. 怎么计算一个对象占用的内存
  3. intellij IDEA怎样打war包
  4. 切换不了摄像头 高拍仪_手机摄像头模组支架保护膜的变革之路
  5. UA OPTI501 电磁波4 电介质及其极化
  6. keras、tf、numpy实现logloss对比
  7. BDS-HA:构建高可用、低延迟的HBase服务
  8. 你家的饮水机,到底可以有多脏?
  9. 用条件运算符编写java程序_Java 编程入门课程丨第 8 单元:条件运算符和控制语句...
  10. iOS开发Item属性总结
  11. 《孙子兵法》【火攻第十二】
  12. 项目的权限设计的小计
  13. Django-开胃菜
  14. Mycat-server-1.6.5 常见分片方式
  15. 复制slave-skip-errors及error查看
  16. 【滤波器】基于matlab GUI分数延迟滤波器设计【含Matlab源码 1347期】
  17. 案例解读|江苏银行—智多星大数据分析云平台实践
  18. 短信API接口比较常见的回调状态
  19. double比较大小
  20. iPhone屏幕适配 新增iPhone XS iPhone XR iPhone XS Max

热门文章

  1. zabbix 报警 Lack of free swap space on Zabbix server 处理
  2. 【Python】【pygame】更逼真的星星、连绵细雨
  3. 计算机的经历和灵感,从电脑编程灵感中得到的启发
  4. 2017-11-18 借白银说点市场心得
  5. python 插值求包络线
  6. 经历“海潮效应”,云图如何成为智能家居界的苹果?
  7. wireshark抓包分析IP数据报
  8. 红杉资本合伙人Maguire:Crypto将是未来30年最大趋势 |链捕手
  9. 怎么样用计算机弹出小星星,【钢琴入门自写教程 1】小星星弹奏
  10. Atom-beautify插件的安装,使用过程