TN062:Windows 控件的消息反射

注释

自联机文档中首次包含此说明以来,尚未更新以下技术说明。 因此,某些过程和主题可能过期或不正确。 有关最新信息,建议在在线文档索引中搜索您感兴趣的主题。

此技术说明介绍消息反射,这是 MFC 4.0 中的一项新功能。 它还包含创建使用消息反射的简单可重用控件的说明。

此技术说明不讨论消息反射,因为它适用于 ActiveX 控件(以前称为 OLE 控件)。 请参阅 ActiveX 控件:子类化 Windows 控件的文章

什么是消息反射

Windows 控件经常将通知消息发送到其父窗口。 例如,许多控件向父控件发送控件颜色通知消息(WM_CTLCOLOR或其变体之一),以允许父控件提供画笔来绘制控件的背景。

在 Windows 和版本 4.0 之前的 MFC 中,父窗口(通常是一个对话框)负责处理这些消息。 这意味着处理消息的代码需要位于父窗口的类中,并且必须在需要处理该消息的每个类中重复该代码。 在上述情况下,希望具有自定义背景的控件的每个对话框必须处理控件颜色通知消息。 如果可以编写可处理其自己的背景色的控件类,那么重用代码会更容易。

在 MFC 4.0 中,旧机制仍然有效 — 父窗口可以处理通知消息。 但是,MFC 4.0 通过提供一项称为“消息反射”的功能来方便重复使用,该功能允许在子控件窗口或父窗口或两者中处理这些通知消息。 在控件背景色示例中,现在可以编写一个控件类,该控件类通过处理反射WM_CTLCOLOR消息来设置自己的背景色,这一切都无需依赖父级。 (请注意,由于消息反射由 MFC 而不是 Windows 实现,因此必须派 CWnd 生父窗口类才能使消息反射正常工作。

旧版本的 MFC 通过为一些消息提供虚拟函数(例如所有者绘制列表框(WM_DRAWITEM等)提供虚拟函数来执行类似于消息反射的内容。 新的消息反射机制已通用化且一致。

消息反射与为 4.0 之前的 MFC 版本编写的代码向后兼容。

如果在父窗口的类中为特定消息或消息范围提供了处理程序,则它将替代反映的消息处理程序,前提是你未在自己的处理程序中调用基类处理程序函数。 例如,如果在对话框类中处理WM_CTLCOLOR,则处理将覆盖任何反映的消息处理程序。

如果在父窗口类中,为特定WM_NOTIFY消息或一系列WM_NOTIFY消息提供处理程序,则仅当子控件发送这些消息的子控件没有通过反射的消息处理程序时,才会调用处理程序 ON_NOTIFY_REFLECT()。 如果在 ON_NOTIFY_REFLECT_EX() 消息映射中使用,则消息处理程序可能不允许父窗口处理消息。 如果处理程序返回 FALSE,则消息也将由父级处理,而返回 TRUE 的调用不允许父级处理它。 请注意,反映的消息在通知消息之前处理。

发送WM_NOTIFY消息时,会提供第一个处理控件的机会。 如果发送了任何其他反映的消息,父窗口将第一次有机会处理它,控件将收到反映的消息。 为此,它需要处理程序函数和控件的类消息映射中的相应条目。

反映消息的消息映射宏与常规通知略有不同:它 已_REFLECT 追加到其常规名称。 例如,若要在父级中处理WM_NOTIFY消息,请使用父消息映射中的宏ON_NOTIFY。 若要在子控件中处理反映的消息,请使用子控件的消息映射中的ON_NOTIFY_REFLECT宏。 在某些情况下,参数也不同。 请注意,ClassWizard 通常可以为你添加消息映射条目,并提供具有正确参数的框架函数实现。

有关新WM_NOTIFY 消息的信息,请参阅 TN061:ON_NOTIFY和WM_NOTIFY消息

Message-Map 反射消息的条目和处理程序函数原型

若要处理反映的控制通知消息,请使用下表中列出的消息映射宏和函数原型。

ClassWizard 通常可以为你添加这些消息映射条目,并提供主干函数实现。 有关如何为反射消息定义处理程序的信息,请参阅为 反射消息定义消息处理程序

若要从消息名称转换为反映的宏名称,请追加 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. 多个反映的消息处理程序所需的参数比父窗口中的类似处理程序少。 只需将下表中的名称与文档中正式参数的名称匹配。

地图条目 函数原型
ON_CONTROL_REFLECT(wNotifyCodememberFxn afx_msg voidmemberFxn( );
ON_NOTIFY_REFLECT(wNotifyCodememberFxn afx_msg voidmemberFxn(NMHDR*pNotifyStruct、LRESULT*result;
ON_UPDATE_COMMAND_UI_REFLECT(memberFxn afx_msg voidmemberFxn(CCmdUI*pCmdUI;
ON_WM_CTLCOLOR_REFLECT() afx_msg HBRUSH CtlColor (CDC*pDC、UINTnCtlColor;
ON_WM_DRAWITEM_REFLECT() afx_msg void DrawItem (LPDRAWITEMSTRUCTlpDrawItemStruct):
ON_WM_MEASUREITEM_REFLECT() afx_msg void MeasureItem (LPMEASUREITEMSTRUCTlpMeasureItemStruct):
ON_WM_DELETEITEM_REFLECT() afx_msg void DeleteItem (LPDELETEITEMSTRUCTlpDeleteItemStruct):
ON_WM_COMPAREITEM_REFLECT() afx_msg int CompareItem (LPCOMPAREITEMSTRUCTlpCompareItemStruct):
ON_WM_CHARTOITEM_REFLECT() afx_msg int CharToItem (UINT、UINTnIndexnKey):
ON_WM_VKEYTOITEM_REFLECT() afx_msg int VKeyToItem (UINT、UINTnIndexnKey):
ON_WM_HSCROLL_REFLECT() afx_msg void HScroll (UINT、UINTnPosnSBCode):
ON_WM_VSCROLL_REFLECT() afx_msg void VScroll (UINT、UINTnPosnSBCode):
ON_WM_PARENTNOTIFY_REFLECT() afx_msg void ParentNotify (UINTmessage、LPARAMlParam):

ON_NOTIFY_REFLECT和ON_CONTROL_REFLECT宏具有变体,允许多个对象(如控件及其父对象)处理给定消息。

地图条目 函数原型
ON_NOTIFY_REFLECT_EX(wNotifyCodememberFxn afx_msg BOOLmemberFxn(NMHDR*pNotifyStruct、LRESULT*result;
ON_CONTROL_REFLECT_EX(wNotifyCodememberFxn afx_msg BOOLmemberFxn( );

处理反射消息:可重用控件的示例

这个简单示例创建一个名为 CYellowEdit 的可重用控件。 该控件的工作方式与常规编辑控件相同,只不过它在黄色背景上显示黑色文本。 添加允许控件显示不同颜色的成员函数 CYellowEdit 很容易。

尝试创建可重用控件的示例

  1. 在现有应用程序中创建新对话框。 有关详细信息,请参阅 对话框编辑器 主题。

    必须具有开发可重用控件的应用程序。 如果没有要使用的现有应用程序,请使用 AppWizard 创建基于对话框的应用程序。

  2. 将项目加载到 Visual C++后,使用 ClassWizard 创建基于CEdit调用CYellowEdit的新类。

  3. 向类添加三个成员变量 CYellowEdit 。 前两个将是 COLORREF 变量,用于保存文本颜色和背景色。 第三个对象是用于 CBrush 绘制背景的画笔的对象。 该 CBrush 对象允许你创建一次画笔,只需在之后引用该画笔,并在销毁控件时 CYellowEdit 自动销毁画笔。

  4. 通过编写构造函数来初始化成员变量,如下所示:

    CYellowEdit::CYellowEdit()
    {
        m_clrText = RGB(0, 0, 0);
        m_clrBkgnd = RGB(255, 255, 0);
        m_brBkgnd.CreateSolidBrush(m_clrBkgnd);
    }
    
  5. 使用 ClassWizard,向类添加反射WM_CTLCOLOR消息 CYellowEdit 的处理程序。 请注意,可以处理的消息列表中的消息名称前面的等号指示消息已反映。 这在 为反射消息定义消息处理程序中进行了介绍。

    ClassWizard 为你添加以下消息映射宏和主干函数:

    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;
    }
    
  6. 将函数的正文替换为以下代码。 该代码指定控件其余部分的文本颜色、文本背景色和背景色。

    pDC->SetTextColor(m_clrText);   // text
    pDC->SetBkColor(m_clrBkgnd);    // text bkgnd
    return m_brBkgnd;               // ctl bkgnd
    
  7. 在对话框中创建编辑控件,然后在按住控件键时双击编辑控件,将其附加到成员变量。 在“添加成员变量”对话框中,完成变量名称,然后为类别选择“Control”,然后为变量类型选择“CYellowEdit”。 不要忘记在对话框中设置 Tab 键顺序。 此外,请确保在对话框的头文件中包括控件的头文件 CYellowEdit

  8. 生成并运行应用程序。 编辑控件将具有黄色背景。

另请参阅

按编号列出的技术说明
按类别列出的技术说明