Windows控件的消息反射


本文翻译自MSDN中TN062: Message Reflection for Windows Controls

一 什么是消息反射

Windows控件需要频繁的向父窗口发送通告消息(Notification Message)。例如,很多控件为了改变背景色,需要给父窗口发送WM_CTLCOLOR 这个通告消息,以允许父窗口提供用户绘制控件背景色的画刷。在Windows4.0 和MFC4.0以前,父窗口(通常是一个对话框)负责处理这些通告消息。这意味必须在父窗口类中提供通告消息的消息处理函数;如果多个父窗口都需要处理这个消息,则必须在每个父窗口类中都提供相同的代码。以上面为例,每个想要修改控件背景色的对话框都需要处理WM_CTLCOLOR 消息。因此,如果一个控件类能处理它自己的背景颜色,那么重用代码会容易得多。
在MFC4.0中,旧的机制依然有效——父窗口类可以处理通告消息。另外,MFC4.0为了便于代码重用,提供了一种称为“消息反射”(message reflection)的机制,这种机制允许控件类或者父窗口、或者两者去处理通告消息。以上述修改控件背景色为例,现在你可以写一个控件类,在这个类中可以通过处理反射回来的WM_CTLCOLOR 消息来修改自己的背景色,所有这一切都无需依赖父窗口。(注意因为消息反射是被MFC实现的,不是Windows,所以为了确保消息反射可以工作,父窗口类必须从CWnd类继承而来)。
旧版本的 MFC 通过为一些消息提供虚函数来执行类似于消息反射的操作,例如用于自绘列表框的消息(WM_DRAWITEM 等)。 新的消息反射机制是通用的和一致的。另外,消息反射机制向后兼容MFC4.0之前的代码。
如果在你的父窗口中为特定或者一系列消息提供了消息处理函数,并且你没有在这些处理函数中调用基类的处理函数,它将覆盖相同消息的反射消息处理函数。例如,如果在对话框类中处理了WM_CTLCOLOR 消息,这将覆盖所有的反射消息处理函数。
如果在父窗口类提供了处理指定或者一系列WM_NOTIFY 消息的函数,只有当发送这些消息的子控件没有通过ON_NOTIFY_REFLECT()宏提供反射消息处理函数时,父窗口的这些消息处理函数才会被调用。如果在控件类使用了ON_NOTIFY_REFLECT_EX() 宏来提供消息处理函数,这个处理函数通过返回值确定是否调用父窗口的消息处理函数;如果返回值为FALSE,消息将会传递给父窗口,如果为TRUE,则不会传递给父窗口。请注意,反射消息的处理先于通告消息。
当WM_NOTIFY发送后,控件会优先处理它。其他反射消息发出后,父窗口会优先处理它,然后控件才接收到该消息(译者注:控件能否收到消息,要看父窗口是否调用了基类处理函数)。为了完成上述目的,需要在控件类的消息映射表中提供相应的条目,并且在控件类中提供相应的处理函数。
反射消息的消息映射宏和常规的通告消息映射宏稍有不同。比如,在父窗口中处理WM_NOTIFY 消息,应在父窗口类中使用 ON_NOTIFY 宏。在子控件中处理反射消息,使用ON_NOTIFY_REFLECT 宏。一些情况下,两者的参数也不同。注意类向导可以添加消息映射条目、并提供函数实现框架。

二 反射消息的消息映射表和处理函数原型

为了处理控件反射通告消息,使用下表中的消息映射宏和函数原型(类向导可以完成上述工作)。
从消息名称到反射宏名称的转换方法为:消息名称前加"ON_" ,后面加“_REFLECT”.例如,WM_CTLCOLOR 变为 ON_WM_CTLCOLOR_REFLECT。不过,有三个例外:

  • WM_COMMAND反射消息宏为:ON_CONTROL_REFLECT
  • WM_NOTIFY反射消息宏ON_NOTIFY_REFLECT.
  • ON_UPDATE_COMMAND_UI反射宏为: ON_UPDATE_COMMAND_UI_REFLECT.
    在上述三种情况中,你必须指定处理函数的名称。在其他情况中,你必须使用处理函数的标准名称。
    函数的参数和返回值的含义记录在函数名称或带有 On 前缀的函数名称下。 例如,CtlColor 记录在 OnCtlColor 中。 与父窗口中的类似处理程序相比,一些反射消息处理程序的参数更少。 只需将下表中的名称与文档中的形式参数名称匹配即可。
Map entryFunction prototype
ON_CONTROL_REFLECT( wNotifyCode, memberFxn )afx_msg void memberFxn ( );
ON_NOTIFY_REFLECT( wNotifyCode, memberFxn )afx_msg void memberFxn ( NMHDR * pNotifyStruct, LRESULT* result );
ON_UPDATE_COMMAND_UI_REFLECT( memberFxn )afx_msg void memberFxn ( CCmdUI* pCmdUI );
ON_WM_CTLCOLOR_REFLECT( )afx_msg HBRUSH CtlColor ( CDC* pDC, UINT nCtlColor );
ON_WM_DRAWITEM_REFLECT( )afx_msg void DrawItem ( LPDRAWITEMSTRUCT lpDrawItemStruct );
ON_WM_MEASUREITEM_REFLECT( )afx_msg void MeasureItem ( LPMEASUREITEMSTRUCT lpMeasureItemStruct );
ON_WM_DELETEITEM_REFLECT( )afx_msg void DeleteItem ( LPDELETEITEMSTRUCT lpDeleteItemStruct );
ON_WM_COMPAREITEM_REFLECT( )afx_msg int CompareItem ( LPCOMPAREITEMSTRUCT lpCompareItemStruct );
ON_WM_CHARTOITEM_REFLECT( )afx_msg int CharToItem ( UINT nKey, UINT nIndex );
ON_WM_VKEYTOITEM_REFLECT( )afx_msg int VKeyToItem ( UINT nKey, UINT nIndex );
ON_WM_HSCROLL_REFLECT( )afx_msg void HScroll ( UINT nSBCode, UINT nPos );
ON_WM_VSCROLL_REFLECT( )afx_msg void VScroll ( UINT nSBCode, UINT nPos );
ON_WM_PARENTNOTIFY_REFLECT( )afx_msg void ParentNotify ( UINT message, LPARAM lParam );

ON_NOTIFY_REFLECT 和ON_CONTROL_REFLECT 宏具有一些变体,以允许多个对象(比如控件和其父窗口)处理指定消息。

Map entryFunction prototype
ON_NOTIFY_REFLECT_EX( wNotifyCode, memberFxn )afx_msg BOOL memberFxn ( NMHDR * pNotifyStruct, LRESULT* result );
ON_CONTROL_REFLECT_EX( wNotifyCode, memberFxn )afx_msg BOOL memberFxn ( );

三 处理反射消息的例子:一个可以重用的编辑控件

这个简单的例子创建了一个可重用的控件,名称为CYellowEdit。这个控件和CEdit控件基本相同,只是背景是黄色的。添加允许 CYellowEdit 控件显示不同颜色的成员函数会很容易。
具体步骤为:

  1. 在程序中添加一个对话框
  2. 用类向导创建一个派生于CEdit的类CYellowEdit
  3. 在CYellowEdit类中添加3个数据成员变量,前两个为 COLORREF 变量,用于保存文字颜色和背景颜色。第三个成员变量为CBrush对象,保存背景色的画刷。CBrush对象近允许创建一次画刷,之后仅仅只是引用它;当CBrush对象销毁时画刷资源自动销毁。
  4. 在类的构造函数中进行变量初始化
CYellowEdit::CYellowEdit()
{
  m_clrText = RGB( 0, 0, 0 );
  m_clrBkgnd = RGB( 255, 255, 0 );
  m_brBkgnd.CreateSolidBrush( m_clrBkgnd );
}
  1. 利用类向导,在CYellowEdit中添加反射WM_CTLCOLOR 消息的处理函数。注意,消息名称列表中带“=”的消息表示这是个反射消息。类向导添加了如下代码
ON_WM_CTLCOLOR_REFLECT()

// Note: other code will be in between....

HBRUSH CYellowEdit::CtlColor(CDC* pDC, UINT nCtlColor) 
{
   // TODO: Change any attributes of the DC here
   
   // TODO: Return a non-NULL brush if the
   //   parent's handler should not be called
   return NULL;
}
  1. 将函数体替换为下面代码。下述代码指定文字颜色,文字背景色,控件其余部分的背景色。
   pDC->SetTextColor( m_clrText );   // text
   pDC->SetBkColor( m_clrBkgnd );   // text bkgnd
   return m_brBkgnd;            // ctl bkgnd
  1. 在你的对话框中添加一个编辑框控件,然后给这个编辑框添加一个关联控件变量。在添加成员变量对话框中,将控件类型选为"CYellowEdit"。别忘了设置对话框TAB顺序。当然,确保在对话框头文件中包含CYellowEdit类的头文件。
  2. 编译运行。编辑框的背景已变为黄色。
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值