前言
学习WPF的过程中遇到了事件、命令这些概念,而书中在介绍这些概念时又扯出了新概念——消息(也可以叫旧概念,因为这个机制比较老旧,但由于我没有学习过WinForms编程,所以对我来说是新概念)。那么消息与事件机制各自是怎样的,有什么区别呢?本文结合书中提到的和网上的文来整理一下这些概念。
消息驱动
场景
首先说一个Windows编程中常见的场景,点击窗体上的一个按钮,然后程序执行一定的操作。
非常简单通用的场景,相信在Windows上进行过GUI编程都实操过。
分析
这个过程,从程序设计的观点来看,分以下几步:
- 点击按钮这个操作(或者说这个事件,甚至也能称消息,但这个事件是人(用户)的操作,还不能直接对应到windows的事件消息WM_CLICK)
Notes:- 消息有两重概念:
1.1. 事件本身(点击按钮这个操作及发生的时间点),是一个动态的概念
1.2. 消息内容(指的是信息体),是一个静态的概念 - 当然,消息不只是由用户输入而产生的,应用程序也可以产生消息,系统自身也可以产生消息。这里为了贴合以上场景,专门针对该场景说明。
- 消息有两重概念:
- 该操作会被Windows系统监测到,至于怎么监测不细讲(跟硬件有关),可以认为Windows系统一直在监测这些设备
- 然后Windows系统产生一条消息
Notes:
3.1. 如果把Windows监测用户操作这步看作独立/看作第三方监测的话,消息也可以认为是Windows分发而不是产生的。Windows将第三方监测到的消息,分发给应用程序。
3.2. 这步的消息可以看成一条数据,C语言中的话可以认为是一个结构体,里面记录了消息类型,和一些其他参数
struct Msg msg;
msg.type = xxx;
msg.p1 = ...;
...
- 将该消息发给应用程序的一个消息队列
// 当然c中不会这么写,这么写牺牲了准确度,为了方便理解
userProgram.queueMsg.Add(msg);
- 应用程序从队列中取出消息
// 可以理解为应用程序有一个线程,一直循环着从消息队列取消息
while(condition)
{
...
GetMsgFromQueue(queueMsg);
...
}
- 根据消息类型分流到一个分支语句的末端,执行对应的函数,分支语句就类似于,
// 取出消息后,分流,或对应到系统已经给好的函数,或对应到你自定义的函数
switch(msg)
{
...
case msg:
UserOperation();
break;
...
}
我将上面步骤简化成一个简单的示意图,
从上面这图中划一条竖线,将整个过程分为用户操作部分和系统内运行部分,可以看到在系统部分后面步骤的执行可以认为是产生消息开始的,也就是由这条消息去驱使整个过程的推进。故可以称为消息驱动。
最后用文字形式,再简单梳理一遍消息驱动:
用户操作->系统捕捉->产生消息(分发消息)->应用程序获取消息->解析消息执行相应操作
事件驱动
在《深入浅出WPF》一书中,有说事件的前身是消息,Windows是消息驱动的操作系统。这就意味着,把事件和消息看成是两种完全不同的机制是行不通的。
对于WPF中的事件模型,我的理解是本质上还是消息模型,但是微软进行封装,屏蔽细节,最终形成了让程序员看起来、使用起来更简便的事件模型。不需要关注消息的实际产生,系统的分发,消息类型的匹配分流,只需要关注事件的拥有者响应者以及订阅关系。比如在上节的消息驱动过程,我在此可以简化为:
- 将按钮的点击事件Click与窗体的button_OnClick事件处理器关联起来。
button.Click += button_OnClick;
- 窗体上的一个按钮被点击了,按钮激发了点击事件Click
- 窗体接收到了点击事件,执行button_OnClick。
显然,步骤非常简单,底层细节也没有那么多了。
参考资料
- 《深入浅出WPF》,刘铁猛
- 百度百科-消息驱动词条
- 一些博文