Windows 消息循环(2) - WPF中的消息循环
接上文: Windows 消息循环(1) - 概览
win32/MFC/WinForm/WPF 都依靠消息循环驱动,让程序跑起来。
本文介绍 WPF 中是如何使用消息循环来驱动程序的。

4 消息循环在 WPF 中的应用
4.1 引入
只听说过 Dispatcher ,哪里来的消息循环?
先瞧一眼 WPF 启动运行堆栈:

可以发现 PushFrameImpl 这个方法。
去看其源码,就发现了熟悉的消息循环 :

可以理解为:Dispatcher 对消息循环的操作进行了“封装” 。
那,Dispatcher 是谁?
4.2 Dispatchcer
Provides services for managing the queue of work items for a thread.
提供用于管理线程工作项队列的服务。
大部分WPF对象,都是 DispatcherObject。这意味着,可以在 DispatcherObject 中(如 Window 中),
使用 this.Dispatchcer 获取到 Dispatchcer 。

一般我们会通过三种方式获取 Dispatchcer :
// App.Current.Dispatcher;(Application.Current.Dispatcher)
var dispatcher1 = App.Current.Dispatcher;
// CurrentDispatcher;
var dispatcher2 = System.Windows.Threading.Dispatcher.CurrentDispatcher;
// System.Windows.Threading.DispatcherObject.Dispatcher;
var dispatcher3 = this.Dispatcher;
可分为两类:
当前线程的 Dispatcher:
System.Windows.Threading.Dispatcher.CurrentDispatcher;创建对应对象的 Dispatcher:
App.Current.Dispatcher;
DispatcherObject.Dispatcher;
可参见:
Why not Dispather.CurrentDispatcher - haungtengxiao
Dispatcher 和线程是什么关系?
- Dispatcher 属于线程(与线程一一对应)。
- WPF的对象在获取this.Dispatcher属性时,不同对象取的都是同一个Dispatcher实例。(因为都是同一个UI线程创建的。)
- 在默认的 WPF UI线程中: App.Current.Dispatcher = DispatcherObject.Dispatcher
所有的线程(UI线程,普通线程)都有 Dispatcher 吗?
是的。
在所有线程中,调用 System.Windows.Threading.Dispatcher.CurrentDispatcher
都会得到一个属于这个线程的 Dispatcher 对象。(不用的时候不会创建)
所以:如果你想在一个后台线程中,使用 Dispatcher.CurrentDispatcher.Invoke
将操作封送到 UI 线程,是做不到的。因为这时候获取到的 Dispatcher 不是UI线程的 Dispatcher, 而是当前线程自己的 Dispatcher。
4.3 Dispatcher 如何实现跨线程的调用。
最常使用 Dispatcher 的创建就是,在后台线程更新 UI ,那 Dispatcher 是如何做到的呢。
当你调用
Application.Current.Dispatcher.Invoke(() =>
{
SendMessageBtn.Content = "更新按钮";
});
时,Dispatcher 究竟做了什么,把操作转移到 UI 线程上去了。
关于 Invoke,InvokeSync,BeginInvoke 的区别,参见:
深入了解 WPF Dispatcher 的工作原理(Invoke/InvokeAsync 部分) - walterlv

将调用的Delegate和优先级包装成一个DispatcherOperation放入Dispatcher维护的优先级队列当中,这个Queue是按DispatcherPriority排序的,总是高优先级的DispatcherOperation先被处理。
往当前线程的消息队列当中Post一个名为MsgProcessQueue的Message。(这个消息是WPF自己定义的。)这个消息被Post到消息队列之前,还要设置MSG.Handle,这个Handle就是Window 1#的Handle。指定Handle是为了在消息循环Dispatch消息的时候,指定哪个窗口的 WndProc 处理这个消息。
消息循环读取消息。
系统根据获取消息的Handle,发现跟Window1#的Handle相同,那么这个消息派发到Window1#的窗口过程,让其处理。
在窗口过程中,优先级队列当中取一个DispatcherOperation。
执行DispatcherOperation.Invoke方法,Invoke方法的核心就是调用DispatcherOperation构造时传入的Delegate,也就是Dispatcher.BeginInvoke传入的Delegate。最终这个Foo()方法就被执行了。
4.4 回顾
- WPF 底层仍然靠信息循环来驱动。
- Dispatcher 使用消息循环来实现跨进程的委托调用。
- Dispatcher 属于线程,需要理解当前拿到的 Dispatcher 到底是哪个 Dispatcher 。
参考资料:
SendMessage、PostMessage原理-大白菜-51CTO博客
WPF的消息机制(一)- 让应用程序动起来 - 葡萄城技术团队 - 博客园
WPF的消息机制(二)- WPF内部的5个窗口之隐藏消息窗口 - 葡萄城技术团队 - 博客园
WPF的消息机制(三)- WPF内部的5个窗口之处理激活和关闭的消息窗口以及系统资源通知窗口 - 葡萄城技术团队 - 博客园
Why not Dispather.CurrentDispatcher - haungtengxiao
深入了解 WPF Dispatcher 的工作原理(Invoke/InvokeAsync 部分) - walterlv
Windows 消息循环(2) - WPF中的消息循环的更多相关文章
- php中的for循环和js中的for循环
php中的for循环 循环100个0 for ($i=0;$i<=100;$i++){ $pnums.='0'.","; } js中的for循环,循环31个相同的数.循环日期 ...
- 在WPF中处理Windows消息
在Winform中 处理Windows消息通过重写WndProc方法 在WPF中 使用的是System.Windows. Sytem.Windows.Controls等名字空间,没有WndProc函数 ...
- 安卓中的消息循环机制Handler及Looper详解
我们知道安卓中的UI线程不是线程安全的,我们不能在UI线程中进行耗时操作,通常我们的做法是开启一个子线程在子线程中处理耗时操作,但是安卓规定不允许在子线程中进行UI的更新操作,通常我们会通过Handl ...
- 消息队列一:为什么需要消息队列(MQ)?
为什么会需要消息队列(MQ)? #################################################################################### ...
- RabbitMQ消息队列(二)-RabbitMQ消息队列架构与基本概念
没错我还是没有讲怎么安装和写一个HelloWord,不过快了,这一章我们先了解下RabbitMQ的基本概念. RabbitMQ架构 说是架构其实更像是应用场景下的架构(自己画的有点丑,勿嫌弃) 从图中 ...
- MQ消息机制如何确认消费了消息?
消息队列如何保证消息能百分百成功被消费 目前常用的消息队列有很多种,如RabbitMQ,ActiveMQ,Kafka...下面以RabbitMQ为例来讲如何保证消息队列中的信息能百分百被消费掉. 其中 ...
- 【转】深入Windows内核——C++中的消息机制
上节讲了消息的相关概念,本文将进一步聊聊C++中的消息机制. 从简单例子探析核心原理 在讲之前,我们先看一个简单例子:创建一个窗口和两个按钮,用来控制窗口的背景颜色.其效果 图1.效果图 Win32 ...
- TMsgThread, TCommThread -- 在delphi线程中实现消息循环
http://delphi.cjcsoft.net//viewthread.php?tid=635 在delphi线程中实现消息循环 在delphi线程中实现消息循环 Delphi的TThread类使 ...
- Chrome中的消息循环
主要是自己做个学习笔记吧,我经验也不是很丰富,以前学习多线程的时候就感觉写多线程程序很麻烦.主要是线程之间要通信,要切线程,要同步,各种麻烦.我本身的工作经历决定了也没有太多的工作经验,所以chrom ...
随机推荐
- 洛谷 U3357 C2-走楼梯
https://www.luogu.org/problem/show?pid=U3357 题目背景 在你成功地解决了上一个问题之后,方方方不禁有些气恼,于是他在楼梯上跳来跳去,想要你求出他跳的方案数. ...
- HDU 2138 Miller-Rabin 模板题
求素数个数. /** @Date : 2017-09-18 23:05:15 * @FileName: HDU 2138 miller-rabin 模板.cpp * @Platform: Window ...
- Linux 操作系统下 VI 编辑器常用命令详细介绍
一.Vi 简介 vi是unix世界中最通用的全屏编辑器,linux中是用的是vi的加强版vim,vim同vi完全兼容,vi就是"visual interface"的缩写.它可以执行 ...
- 26、Python的可变类型和不可变类型?
Python的每个对象都分为可变和不可变 可变:列表.字典 不可变:数字.字符串.元祖 对不可变类型的变量重新赋值,实际上是重新创建一个不可变类型的对象,并将原来的变量重新指向新创建的对象(如果没有其 ...
- 前端bootstrap框架禁用响应式的方法
在Bootstrap中极其重要的一个技术内容便是响应式布局了,一次编码针对不同设备终端的强大能力使得响应式技术愈发流行. 不过正所谓“萝卜青菜各有所爱”,如果你想要使用Bootstrap开发自己的项目 ...
- http://www.onvif.org/onvif/ver20/util/operationIndex.html
http://www.onvif.org/onvif/ver20/util/operationIndex.html
- Vuex-Action
Action 类似于 mutation,不同在于: Action 提交的是 mutation,而不是直接变更状态. Action 可以包含任意异步操作. 让我们来注册一个简单的 action: con ...
- Fedora8 U盘安装
(一)分区 在XP下"我的电脑“管理功能,对硬盘分区,目的是从逻辑分区中拿出20G空间,分成3个盘(必须为逻辑盘): (1)512MB 用作Linux swap分区: (2)200MB ...
- utsrelease.h 包含svn信息
utsrelease.h是一个自动生成的文件,没有办法修改,但这个数据是根据Makefile和.config的内容进行生成的,通过修改这两个文件的内容,可以改变!/usr/src/linux/Make ...
- kvm命令参数记录
/usr/libexec/qemu-kvm -cpu host -m 1024 -enable-kvm -drive file=/var/lib/libvirt/images/zxc_linux1.i ...