简单说,父窗口把子窗口(控件)发送给父窗口的消息回传给子窗口的回传行为称为消息反射机制。
为什么MFC要使用消息反射机制?简单说,是MFC考虑到实际情形,为提高MFC程序设计质量(如封装性)而设计的一种机制。我们以如下实例来说明。
有一个对话框窗口D,内有一个按钮控件B(ID为IDC_B1),当我们单击按钮B,就我们此时的关注点来说大致会产生如下消息:
1、B:WM_LBUTTONDOWN
2、B:WM_LBUTTONUP
3、D:WM_COMMAND。此消息包含这些信息:通知码:BN_CLICKED;控件ID:IDC_B1;B的句柄。
如果程序员有两个任务,一:对按钮B这一类控件的单击事件编程,如何进行?二:对对话框窗口D的按钮B这个具体按钮实例单击事件编程,如何进行?
任务一
对于程序员来说,要本身作为一个子窗口的按钮B响应一次单击事件,差不多要对WM_LBUTTONDOWN、WM_LBUTTONUP两个消息编程,不方便。
于是我们想到,可以在父窗口D中响应WM_COMMAND来完成一次完整的单击事件处理。但是:1)、需要对WM_COMMAND消息进行分析,确认是按钮B这个类别产生的消息才能进行相应的处理,繁琐;2)、对按钮B这个类别的处理代码放在父窗口类D中实现,不利于代码的封装,因为这里的处理逻辑只与按钮B类相关。
MFC的反射机制就很好地满足了任务一的实现。MFC把由子窗口按钮B产生的WM_COMMAND消息反射给按钮B进行处理,这样,就可以在按钮B的类代码增加反射消息处理成员函数BN_CLICKED(MFC ClassWizard中为“=BN_CLICKED”),即可实现对按钮B类的单击事件处理,既方便又实现了代码逻辑封装。
任务二
对对话框窗口D的按钮B这个具体按钮实例单击事件编程,在MFC ClassicWizard中增加控件ID为IDC_B1、消息为BN_CLICKED的窗口D类成员函数来实现。如果话框窗口D中包含多个按钮B类的实例,比如B2(ID为IDC_B2)、B3(ID为IDC_B3)等,则如法炮制,在MFC ClassicWizard中增加控件ID为IDC_B2、IDC_B3消息为BN_CLICKED的窗口D类成员函数即可。这样的处理符合正常的封装逻辑:对话框D窗口中按钮B的不同实例对象对应不同的实际处理逻辑代码。
需要说明的是,目前的MFC代码实现中,任务一和任务二是互斥的,即:如果按钮B如果实现了对反射消息“=BN_CLICKED”的处理,则对话框窗口D的按钮B实例消息BN_CLICKED将得不到处理,因为MFC实现中发现,对话框窗口D发现控件B对“=BN_CLICKED”进行了处理,则不再调用按钮B实例的BN_CLICKED处理函数。