MFC的消息反射

来源:互联网 发布:如何自学汽车电脑编程 编辑:程序博客网 时间:2024/05/01 18:31

动态生成一个按钮CButton,如何给该按钮添加消息处理,这就用到了消息反射。
学习MFC也一两年了,今天发现自己只是记住方法,而不明白其中的道理。比如今天才知道,拖出一个按钮,利用MFC的Wizard添加消息处理函数,这个按钮的消息不是这个按钮自己处理的,而是他的父窗体替他处理的,所以利用Wizard添加的消息处理函数也是在其父窗体的类中的。
先看windows的消息种类,windows共有3种消息
1.标准消息
除WM_COMMAND之外,所有以WM_开头的消息。从CWnd派生的类,都可以接收到该类消息。
2.命令消息
来自菜单、工具栏按钮或者加速键的消息。这类消息都以WM_COMMAND呈现。在MFC中,通过菜单项的标识(ID)来区分不同的命令消息;在SDK中,通过消息的wParam参数识别。
从CCmdTarget派生的类都可以接收到这类消息。
3.通告消息
由控件产生的消息,例如,按钮的单击,列表框的选择等均产生此类消息,为的是向其父窗口(通常是对话框)通知事件的发生。这类消息也是以WM_COMMAND消息呈现。从CCmdTarget派生的类都可以接收到这类消息。

子窗体被触发时,向父窗体发送一个WM_COMMAND消息,父窗体的窗口函数处理这个消息,进行相关的处理。另外WM_NOTIFY消息也是WINDOWS CONTROL给它的父窗体发的消息,那这两种消息有什么不同呢?WM_COMMAND消息其实是早期的(WIN3.X时代)子窗体消息,子窗体给父窗体发送消息,父窗体就捕获WM_COMMAND来处理子窗体的消息。但是这个消息只包括了有限的信息,例如wParam包括了子窗口ID和通知码,lParam则包括了子窗口句柄,就这点信息了,如果想知道一些额外的信息的话(例如,鼠标点在了子控件的位置)就要借助于其他的WM_*消息。所以对于新型的WIN32控件,微软就增加了一个新的NOTIFICATION消息,这个消息的参数是这样的:wParam包含了控件ID,而lParam则包含了一个结构体的指针,这个结构体是NMHDR结构或者以NMHDR结构为第一项的一个更大的结构体。这样就可以包含了很多的子控件想给父窗体提供的信息了,甚至可以自己去定义这种的结构体。

由于子控件要想干点事情,要通知其父窗体来完成。在windows和MFC4.0版本一下,父窗口(通常是一个对话框)会对这些消息进行处理,换句话说,自控件的这些消息处理必须在父窗口类体内,每当我们添加子控件的时候,就要在父窗口类中复制这些代码,我们可以想象这是多么的复杂,代码是多么的臃肿! 我们可以想象,如果这些消息都让父窗口类去做,父窗口就成了一个万能的神,一个臃肿不堪的代码机,无论如何消息的处理都集中在父窗口类中,会使父窗口繁重无比,但是子控件却无事可做,并且代码也无法重用,这对于一个程序员来讲是多么痛苦的一件事?! 在老版本的MFC中,设计者也意识到了这个问题,他们对一些消息采用了虚拟机制,例如:WM_DRAWITEM,(直接重写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 开头。

在VC6.0中利用ClassWizard添加消息反射
   (1)在ClassWizard中,打开选择项Message Maps;
   (2)在下拉列表Class name中选择你要控制的类;
   (3)在Object IDs中,选中相应的类名;
   (4)在Messages一栏中找到前面带有=标记的消息,那就是反射消息;
   (5)双击鼠标或者单击添加按钮,然后OK!
在VC2008中添加消息反射
  从“属性”窗口中为反射消息定义消息处理程序  
  1   向   MFC   项目添加控件,如列表控件   (List   Control)、rebar   控件、工具栏   (ToolBar)   控件或树控件   (Tree   Control)。    
  2   在“类视图”中,单击控件类的名称。  
  3   在“属性”窗口中,控件类名出现在“类名”列表中。  
  4   单击“消息”按钮显示可用于添加到控件的   Windows   消息。   
  5   在“属性”窗口中向下滚动消息列表,直到可以看到标题“已反映”。或者,单击“类别”按钮并折叠视图以看到“已反映”标题。    
  6   选择要为其定义处理程序的反射消息。反射消息用等号   (=)   标记(看到消息前面的等号,就算找到了啊)。  
  7   在“属性”窗口中,单击右列中的单元格以将建议的处理程序名称显示为   <add>HandlerName。(例如,=WM_CTLCOLOR   消息处理程序建议   <add>CtlColor   名称。)  
  8   单击建议的名称以接受。处理程序被添加到项目中。     
    已添加的消息处理程序名称出现在反射消息窗口的右列中。    
  9   若要编辑或删除消息处理程序,请重复第   4   到第   7   步。单击包含要编辑或删除的处理程序名称的单元格并单击适当的任务。
 消息处理的过程
  (1)子窗口向父窗口发送通知消息,激发父窗口去调用它的虚函数CWnd::OnNotify。大致的结构如下
    BOOL CWnd::OnNotify(WPARAM wParam, LPARAM lParam, LRESULT* pResult)
    {
      if (ReflectLastMsg(hWndCtrl, pResult)) file://hWndCtrl,为发送窗口
                   return TRUE; file://如果子窗口已处理了此消息,返回
      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",运行,看到了什么?呵呵。

如果为某控件添加了消息反射,同时也在父窗体中添加了该控件的消息处理函数,由于消息是先反射给控件处理的,所以最终只有控件处理该消息,而父窗体的消息处理函数将不再起作用。
引用
http://blog.csdn.net/hnhyhongmingjiang/archive/2008/02/28/2129114.aspx
http://hi.baidu.com/yq_sun2008/blog/item/7af84aeef7a4fe202cf53416.html

原创粉丝点击