1、消息反射解释:

  父窗口将子窗口发给它的通知消息,首先反射回子窗口进行处理(即给子窗口一个机会,让子窗口处理此消息),这样通知消息就有机会能被子窗口自身进行处理。

2、MFC中引入消息反射的原因:

  在Windows的消息处理中,子窗口的发给其父窗口的通知消息只能由其父窗口进行处理,这使得子窗口的自身能动性大大降低(你想,它连改变自己的背景色,处理一个自身滚动问题都要其父窗口来完成),为了解决这个问题,在MFC中引入了 反射消息 “Reflect Message”的概念,进行消息反射,可以使得控制子窗口能够自行处理与自身相关的一些消息,增强了封装性,从而提高了控制子窗口的可重用性。

消息反射的处理流程(不考虑OLE控制)

一、消息反射处理流程图:

  1、父窗口收到控制子窗口发来的通知消息后,调用它的虚函数CWnd::OnNotify.

CWnd::OnNotify()主体部分:

{

//此时,hWndCtrl,为发送窗口,即子窗口的窗口句柄

if (ReflectLastMsg(hWndCtrl, pResult))

return TRUE; // 子窗口已处理了此消息

AFX_NOTIFY notify;

notify.pResult = pResult;

notify.pNMHDR = pNMHDR;

return OnCmdMsg(nID, MAKELONG(nCode, WM_NOTIFY), ¬ify, NULL);

}

  分析:首先,调用ReflectLastMsg(hCtrlChildWnd,...)给子窗口一个自身处理的机会,将消息反射给子窗口处理,函数返回 TRUE,表明子窗口处理了此消息。反之,表示子窗口未处理此消息,此时,调用OnCmdMsg(...)由父窗口进行通常的处理。

  2、ReflectLastMsg中:

  主要是调用发送窗口的SendChildNotifyLastMsg(...)。

  3、SendChildNotifyLastMsg 中:

  调用发送窗口的虚函数OnChildNotify函数,进行处理。 如果没有处理,则调用ReflectChildNotify(...)函数进行标准的反射消息 的消息映射处理。

二、消息处理

方式1:

  由上述处理流程可以看出来,子窗口要想自身处理此消息,重载子控件窗口的OnChildNotify虚拟函数应该是很容易想到的方式。

  注意:MFC中对各个子控件窗口一般都已经重载了OnChildNotify函数,它对应调用类的虚函数进行处理,所以,你重载对应的虚函数即可,如下例:

BOOL CStatusBarCtrl::OnChildNotify(UINT message, WPARAM wParam, LPARAM lParam,LRESULT* pResult)

{

if (message != WM_DRAWITEM) //对应不同的控制,会有不同的有特殊处理要求的消息。

return CWnd::OnChildNotify(message, wParam, lParam, pResult);

...

...

DrawItem((LPDRAWITEMSTRUCT)lParam);

return TRUE;

}

virtual void DrawItem( LPDRAWITEMSTRUCT lpDrawItemStruct );

void CStatusBarCtrl::DrawItem(LPDRAWITEMSTRUCT)

{

ASSERT(FALSE); // must override for self draw status bars

}

你重载CSTatusBarCtrl类的DrawItem虚拟函数,即可实现对反射消息 WM_DRAWITEM的处理。

方式2:

  从方式1可以看出,如果你不在被重载的OnChildNotify中对消息进行处理,函数会调用CWnd::OnChildNotify,它调用ReflectChildNotify函数进行标准的处理。

1、增加反射消息 的映射入口。

2、增加对应的消息处理函数。

注意:可以使用MFC的ClassWizard作上述动作,在ClassWizard中,可处理的反射消息 以一个"="号以示区别。返回值为TRUE,表示控件窗口已处理此反射消息 ,为FALSE,表示控件子窗口未处理此反射消息 。

结语:

消息反射不是很难的概念。它仅出现在MFC中;它的用意是方便控制子窗口的重用;对某些通知消息你可以重载对应的虚函数(WM_DRAWITEM...)进行处理;对其它你可以使用标准的消息反射映射进行处理。

http://blog.csdn.net/zhuzhubin/archive/2009/07/17/4358047.aspx

http://www.cublog.cn/u1/33888/showart_320960.html

什么是消息反射?
    在windows里面,子控件经常向父控件发送消息,例如很多子控件要绘制自己的背景,就可能向父窗口发送消息WM_CTLCOLOR。对于从子控件发来的消息,父控件有可能在处理之前,把消息返还给子控件处理,这样消息看起来就想是从父窗口反射回来一样,故此得名:消息反射。

消息反射的由来
    在windows和MFC4.0版本一下,父窗口(通常是一个对话框)会对这些消息进行处理,换句话说,自控件的这些消息处理必须在父窗口类体内,每当我们添加子控件的时候,就要在父窗口类中复制这些代码,我们可以想象这是多么的复杂,代码是多么的臃肿!
    我们可以想象,如果这些消息都让父窗口类去做,父窗口就成了一个万能的神,一个臃肿不堪的代码机,无论如何消息的处理都集中在父窗口类中,会使父窗口繁重无比,但是子控件却无事可做,并且代码也无法重用,这对于一个程序员来讲是多么痛苦的一件事?!
    在老版本的MFC中,设计者也意识到了这个问题,他们对一些消息采用了虚拟机制,例如:WM_DRAWITEM,这样子控件就有机会控制自己的动作,代码的可重用性有了一定的提高,但是这还没有达到大部分人的要求,所以在高版本的MFC中,提出了一种更方便的机制:消息反射。
    通过消息反射机制,子控件窗口便能够自行处理与自身相关的一些消息,增强了封装性,同时也提高了子控件窗口类的可重用性。不过需要注意的是:消息反射是 MFC实现的,不是windows实现的;要让你的消息反射机制工作,你的类必须从CWnd类派生。

Message-Map中的处理
    如果想要处理消息反射,必须了解相应的Message-Map宏和函数原型。一般来讲,Message-Map是有一定的规律的,通常她在消息的前面加上一个ON_ ,然后再消息的最后加上 _REFLECT。例如我们前面提到的WM_CTLCOLOR 经过处理后变成了ON_WM_CTLCOLOR_REFLECT;WM_MEASUREITEM则变成了 ON_WM_MEASUREITEM_REFLECT。
    凡事总会有例外,这里也是这样,这里面有3个例外:
    (1) WM_COMMAND 转换成 ON_CONTROL_REFLECT;
    (2) WM_NOTIFY  转换成 ON_NOTIFY_REFLECT;
    (3) ON_Update_COMMAND_UI 转换成 ON_Update_COMMAND_UI_REFLECT;
    对于函数原型,也必须是以 afx_msg 开头。

利用ClassWizard添加消息反射
    (1)在ClassWizard中,打开选择项Message Maps;
    (2)在下拉列表Class name中选择你要控制的类;
    (3)在Object IDs中,选中相应的类名;
    (4)在Messages一栏中找到前面带有=标记的消息,那就是反射消息;
    (5)双击鼠标或者单击添加按钮,然后OK!

消息处理的过程
  (1)子窗口向父窗口发送通知消息,激发父窗口去调用它的虚函数CWnd::OnNotify。大致的结构如下

BOOL CWnd::OnNotify(WPARAM wParam, LPARAM lParam, LRESULT* pResult)

{

if(ReflectLastMsg(hWndCtrl, pResult))     // hWndCtrl,为发送窗口

return TRUE;                         // 如果子窗口已处理了此消息,返回

AFX_NOTIFY notify;

notify.pResult = pResult;

notify.pNMHDR = pNMHDR;

return OnCmdMsg(nID, MAKELONG(nCode, WM_NOTIFY)? notify:NULL);

}

(2)ReflectLastMsg声明如下:static BOOL PASCAL ReflectLastMsg(HWND hWndChild, LRESULT* pResult = NULL);
     它的主要任务就是调用发送窗口的SendChildNotifyLastMsg。 
(3)SendChildNotifyLastMsg声明如下:BOOL SendChildNotifyLastMsg(LRESULT* pResult = NULL);
     调用发送窗口的虚函数OnChildNotify函数,进行处理。 如果发送窗口没有进行重载处理,则调用ReflectChildNotify(...)函数进行标准的反射消息的消息映射处理。

使用的一个例子
    这里面我们举一个简单的例子,希望大家能够更清晰的掌握消息反射机制。
    (1)创建一个基于对话框的工程。
    (2)利用向导创建一个新的类:CMyEdit,基类是CEdit。
    (3)在CMyEdit头文件中加入3个成员变量:
COLORREF m_clrText ;
COLORREF m_clrBkgnd ;
CBrush   m_brBkgnd;
    (4)利用向导在其中加入WM_CTLCOLOR(看到了么,前面是不是有一个=?),并且将它的函数体改为:

HBRUSH CMyEdit::CtlColor(CDC* pDC, UINT nCtlColor)

{

pDC->SetTextColor( m_clrText );    // text

pDC->SetBkColor( m_clrBkgnd );    // text bkgnd

return m_brBkgnd;                // ctl bkgnd

}

同时我们在.cpp文件中会看到ON_WM_CTLCOLOR_REFLECT(),这就是我们所说的经过处理的宏,是不是很符合规则?
    (5)在对话框中加入一个Edit,增加一个关联的变量,选择Control属性,类别为CMyEdit。
    (6)在对话框.cpp文件中加入#include "MyEdit.h",运行,看到了什么?呵呵。

MFC的消息反射机制的更多相关文章

  1. MFC消息反射机制

    消息反射机制要解决什么问题呢? 消息反射机制主要是为了控件而实现的.每当控件需要某些资讯(比如,绘制自身背景的画刷,显示字体的颜色等等)时,都会频繁地向其父窗口发送通告消息(notification ...

  2. MFC的消息映射机制揭秘

    MFC的设计者们在设计MFC时,紧紧把握一个目标,那就是尽可能使得MFC的代码要小,速度尽可能快.为了这个目标,他们使用了许多技巧,其中很多技巧体现在宏的运用上,实现MFC的消息映射的机制就是其中之一 ...

  3. MFC的消息响应机制说明

    MFC的快速理解: 1.MFC的设计者们在设计MFC时,有一个主要的方向就是尽可能使得MFC的代码要小,速度尽可能快.为了这个方向,工程师们使用了许多技巧,主要表现在宏的运用上,实 现MFC的消息映射 ...

  4. MFC中消息响应机制

    由于视类窗口始终覆盖在框架类窗口之上,因此所有操作,包括鼠标单击.鼠标移动等操作都只能由视类窗口捕获.一个MFC消息响应函数在程序中有三处相关信息:函数原型.函数实现和以及用来关联消息和消息响应函数的 ...

  5. MFC编程入门之五(MFC消息映射机制概述)

    在MFC软件开发中,界面操作或者线程之间通信都会经常用到消息,通过对消息的处理实现相应的操作.比较典型的过程是,用户操作窗口,然后有消息产生,送给窗口的消息处理函数处理,对用户的操作做出响应. 一.什 ...

  6. VS2010/MFC编程入门之五(MFC消息映射机制概述)

    VS2010/MFC编程入门之五(MFC消息映射机制概述)-软件开发-鸡啄米 http://www.jizhuomi.com/software/147.html 上一讲鸡啄米为大家简单分析了MFC应用 ...

  7. MFC消息响应机制 q

    MFC消息响应机制分析 1 引言微软公司提供的MFC基本类库(Microsoft Foundation Classes),是进行可视化编程时使用最为流行的一个类 库.MFC封装了大部分Windows ...

  8. MFC消息响应机制分析

    ---- 摘要: ---- MFC是Windows下程序设计的最流行的一个类库,但是该类库比较庞杂,尤其是它的消息映射机制,更是涉及到很多低层的东西,我们在这里,对它的整个消息映射机制进行了系统的分析 ...

  9. MFC的消息机制

    MFC的消息循环(::GetMessage,::PeekMessage)消息泵(CWinThread::PumpMessage)和MFC的消息在窗口之间的路由是两件不同的事情 分两个步骤完成: 1 “ ...

随机推荐

  1. android之IntentFilter的用法_Intent.ACTION_TIME_TICK在manifest.xml不起作用

    在模仿一个天气预报的widget时候,用到了IntentFilter,感觉在manifest.xml注册的receiver跟用代码写registerReceiver()的效果应该是相同的,于是想证明一 ...

  2. POJ 2777 线段树基础题

    题意: 给你一个长度为N的线段数,一开始每个树的颜色都是1,然后有2个操作. 第一个操作,将区间[a , b ]的颜色换成c. 第二个操作,输出区间[a , b ]不同颜色的总数. 直接线段树搞之.不 ...

  3. Spark Core源代码分析: Spark任务模型

    概述 一个Spark的Job分为多个stage,最后一个stage会包含一个或多个ResultTask,前面的stages会包含一个或多个ShuffleMapTasks. ResultTask运行并将 ...

  4. 华为HCNA教程(笔记)

    第一章 VRP操作基础 1VRP基础 MiniUsb串口连接交换机的方法 2eNSP入门 3命令行基础(1) eNSP中路由开启后(记住port)---第三方软件连接该路由方法:telnet 127. ...

  5. Swift中的ViewController

    ViewController是iOS应用程序中重要的部分,是应用程序数据和视图之间的重要桥梁,ViewController管理应用中的众多视图.iOS的SDK中提供很多原生ViewController ...

  6. 17-UIKit(UIView的动画)

    2. UIView的动画 UIView类本身具有动画的功能 2.1 概念 由UI对底层Core Animation框架的封装 可以轻松简单的实现动画效果 2.2 两种使用方式 1> Block ...

  7. MySQL中innodb引擎分析(初始化)

    MySQL的存储引擎是以插件形式工作的,这应该是MySQL的一大特色了吧! 依据<深入理解MySQL>的内容,5.1版本号时存储引擎的插件化都还不是彻底,确切的说是刚加入的特性.为MySQ ...

  8. mysql导入sql文件过大或连接超时的解决的方法

    前段时间出差在现场开发的时候,导入数据库老是出问题.最后发现了一个奇妙sql语句交给实施,仅仅要导入出错就把例如以下语句运行就能够了.至今屡试不爽. set global max_allowed_pa ...

  9. 使用代码辅助生成工具CodeSmith -- 生成NHibernate的映射文件

    首先下载CodeSmith工具:在百度云中,在CodeSmith文件夹中. 安装,使用激活工具激活. 然后下载NHibernate模板,也是在百度云中,在CodeSmith文件夹中. 之后直接点击NH ...

  10. 搭建Windows SVN服务器及TortoiseSVN使用帮助和下载

    搭建Windows SVN服务器: 用的SVN服务器通常为外部,例如Google Code的服务器,不过,做为一个程序开发人员,就算自己一个人写程序,也应该有一个SVN版本控制系统,以便对开发代码进行 ...