WPF的消息机制(二)- WPF内部的5个窗口之隐藏消息窗口

news/2025/2/26 7:15:55

 

目录

WPF的消息机制(一)-让应用程序动起来

WPF的消息机制(二)-WPF内部的5个窗口

(1)隐藏消息窗口

(2)处理激活和关闭的消息的窗口和系统资源通知窗口

(3)用于用户交互的可见窗口

(4)用于UI窗口绘制的可见窗口

WPF的消息机制(三)-WPF输入事件的来源

WPF的消息机制(四)-WPF中UI的更新

 

WPF内部的5个窗口

对于Windows系统来说,它是一个消息系统,消息系统的核心就是窗口。对于WPF来说也是如此。那么WPF内部为什么需要窗口,又存在哪些窗口呢?

在上一篇,我们频繁的提及“线程”,“Dispatcher”其实,运行WPF应用程序所在的线程就是WPF所谓的UI线程,在Application.Run之后,调用Dispatcher.Run时会检查当前线程是否已经存在了一个Dispatcher对象,如果没有就构造一个,在这里,一个线程对应一个Dispatcher。因此,WPF的对象在获取this.Dispatcher属性时,不同对象取的都是同一个Dispatcher实例。另外,前面提到的“消息循环”,“消息队列”等都是Win32应用程序的概念,我们知道,提起这些概念,必然会跟Win32的“窗口”,“Handle”,“WndProc”之类的概念离不开,那么WPF里面究竟有没有“窗体”,“Handle”,“WndProc”呢?

我想说的是:有,还不止一个,只不过没有暴露出来,外面不需要关心这些。

通常情况下,一个WPF应用程序在运行起来的时候,后台会创建5个Win32的窗口,帮助WPF系统来处理操作系统以及应用程序内部的消息。在这5个窗口中,只有一个是可见的,可以处理输入事件与用户交互,其他4个窗口都是不可见的,帮助WPF处理来自其他方面的消息。接下来我会来介绍究竟这5个Win32的窗口如何帮助WPF处理消息,我会根据每个窗口创建的顺序来介绍。

 

隐藏消息窗口

创建时机:在Application的构造函数调用基类DispatcherObject的构造函数的时候,会创建一个Dispatcher对象,在Dispatcher的私有构造函数当中。

用途:实现WPF线程模型的异步调用。

谈到异步调用,相信许多人都不陌生。WinForm下,我们通常为了使一些花费较多时间的方法调用不影响UI的响应,会将这个操作分为很多步,然后使用BeginInvoke调用每一步,这样UI响应就不会被阻塞。BeginInvoke的本质是往消息队列当中PostMessage,而不是直接调用,与此同时,UI行为(MouseMove)导致系统也往消息队列当中PostMessage更新UI,但由于彼此花费的时间很短,就感觉两个消息是被同时处理似的,界面就不会觉得被阻塞了。WPF同样面临这样的问题,他是如何解决的呢?在这里Window 1#起着至关重要的作用。通过下面一副图我们来看看Window 1#在做什么事情?

clip_image002

WPF也是通过BeginInvoke来解决的,而Wpf的BeginInvoke是在Dispatcher上面暴露了,因为整个消息系统都是Dispatcher在协调。从上面图可以看出Dispatcher在调用BeginInvoke之后所经历的流程,最终是什么时候Foo()被真正执行的。

第一步,就是将调用的Delegate和优先级包装成一个DispatcherOperation放入Dispatcher维护的优先级队列当中,这个Queue是按DispatcherPriority排序的,总是高优先级的DispatcherOperation先被处理。关于优先级相关知识可以参考MSDN对WPF线程模型的解释。

第二步,往当前线程的消息队列当中Post一个名为MsgProcessQueue的Message。这个消息是WPF自己定义的,见Dispatcher的静态构造函数当中的

_msgProcessQueue = UnsafeNativeMethods.RegisterWindowMessage("DispatcherProcessQueue");

这个消息被Post到消息队列之前,还要设置MSG.Handle,这个Handle就是Window 1#的Handle。指定Handle是为了在消息循环Dispatch消息的时候,指定哪个窗口的WndProc(窗口过程)处理这个消息。在这里所有BeginInvoke引起的消息都是Window1#的窗口过程来处理的。

第三步,消息循环读取消息。

第四步,系统根据获取消息的Handle,发现跟Window1#的Handle相同,那么这个消息派发到Window1#的窗口过程,让其处理。

第五步,在窗口过程中,优先级队列当中取一个DispatcherOperation。

第六步,执行DispatcherOperation.Invoke方法,Invoke方法的核心就是调用DispatcherOperation构造时传入的Delegate,也就是Dispatcher.BeginInvoke传入的Delegate。最终这个Foo()方法就被执行了。

通过上面的六步过程,一次Dispatcher.BeginInvoke就被处理完成。而这个过程需要消息不断的流动,就必须加入消息队列,最后还要特定的窗口过程处理,而核心的东西就是这个隐藏的Window1#,他在WPF当中只负责处理异步调用,其他的消息他不关心,剩余的4个窗口在处理。这个Window1#在WPF当中被包了一层壳子,如果感兴趣,你可以去查看类型MessageOnlyHwndWrapper。


http://www.niftyadmin.cn/n/2639140.html

相关文章

(4)剑指Offer之链表相关编程题

一 链表中倒数第k个节点 题目描述: 输入一个链表,输出该链表中倒数第k个结点 问题分析: 一句话概括:两个指针一个指针p1先开始跑,指针p1跑到k-1个节点后,另一个节点p2开始跑,当p1跑到最后时&…

何给背景图像使用CSS3变形

摘要: 如何给背景图像使用CSS3变形呢?在这里,Craig Buckler给出了答案。 CSS3的transform属性可以缩放、倾斜和旋转任何元素。在没有任何浏览器前缀的前提下,这个属性已经被所有的现代浏览器所支持。如果想支持黑莓浏览器和安卓版…

nginx次级域名部署dva静态项目!

这几天为了部署前端项目的事情,头都搞大了。又没有运维的支持,全靠我这个前端开发弄,真是踩了部署的各种坑了。 这里说一下 nginx 次级域名部署基于 dvajs 开发 react 框架的静态项目的注意要点。 基于 dvajs 框架的开发环境搭建,…

软件质量没有银弹:阿里巴巴的25个技术实践与坑

摘要: 在欧洲中世纪的传说中,有一种叫“人狼”的妖怪,就是人面狼身。它们会讲人话,专在月圆之夜去袭击人类。而且传说中对“人狼”用一般的枪弹是不起作用的,普通子弹都伤不到也打不死它,只有一种用银子作成…

asp.net core 框架搭建2-搭建MVC后台管理系统

文章目录 系列文章1.项目搭建1.1 新建Asp.net core MVC项目1.2 ASP.NET Core MVC目录结构1.3 创建一个控制器,与页面数据交互1.4 实现一个登录页面1.5 实现后台管理主界面 2.过程中知识点和涉及到的问题2.1 session的使用2.2 EF Core连接mysql 源码下载 作者&#x…

6月26日云栖精选夜读:成为一名Java高级工程师你需要学什么

摘要: 1.技术广度方面 至少要精通多门开源技术吧,研究过框架等的源码。 2.项目经验方面 从头到尾跟过几个大项目,头是指需求阶段,包括需求调研。 尾是指上线交付之后,包括维护阶段。 1.技术广度方面 至少要精通多门开源…

Python基础-字符串实例

2019独角兽企业重金招聘Python工程师标准>>> 字符串判断 详细列表参考博文《Python基础-运算》 判断字符“小”是否在字符串“小明”中 name "字符串"if "字符" in name:print("字符在字符串中!") else:print("字符…

大象中原

中国文化,100年看上海,1000年看北京,3000年看河南。河南是中华民族的发祥地,其历史悠久,文化厚重,正如河南博物馆的名字:大象中原。当然,大象也很容易联想到“大象”。一、文明曙光​…