Windows 控件的消息反射

来源:互联网 发布:黄石弓箭 淘宝 编辑:程序博客网 时间:2024/05/16 06:46

本技术注意讲述了消息反射,一个 MFC4.0 中的新特色。同时讲述了使用消息反射建立一个简单可重用控件的指导。

       本文并没有就 ActiveX 控件(以前称为 OLE 控件)如何进行消息反射的。有关这方面的资料请参见《ActiveX

       什么是消息反射?

       窗口不断地频繁地发送通知消息给其父窗口。 例如:许多控件会发送控件颜色通知消息(WM_CTLCOLOR 或它的一个变体)给其父窗口以允许其父窗口来提供一个绘制控件背景的刷子。

       在 Windows 和 MFC4.0以前的版本中,父窗口——通常是一个对话框,会来响应处理这些消息。这就意味着处理这些消息的代码需要在父窗口类中实现,而且对于每一个类都要处理这些消息。在上述的情况下,每个需要自定义背景控件的对话框都不得不处理控件颜色通知消息。如果一个控件可以处理自己的背景颜色,而且可以被重用,事情将变的简单的多。

       在 MFC 4.0 中,旧的消息机制仍然在工作 ——父窗口可以处理通知消息。另外同时,MFC 4.0通过提供了一种称为“消息反射”的新机制使的重复使用变的容易多了。消息反射允许这些通知消息既能够被控件自身处理,也能够被其父窗口来处理。在上述控件背景颜色例子中,现在你就既可以通过被发射过来的 WM_CTLCOLOR消息自己写一个能够处理背景颜色的控件类,所有的一切就不再依靠其父窗口了。(请注意,既然这个消息反射只能在 MFC 实现,不是 Windows系统的。所以父窗口必须派生于 CWnd,这样消息反射才能正常工作)。

       MFC 旧版本通过为一些消息提供虚函数的机制部分实现了类似的功能,最典型的就是自绘制的列表框(WM_DRAWITEM 等等)。然后新的消息反射机制更通用和持久。

       消息消息机制向后与 MFC4.0版本前的代码相兼容。

      如果你在控件的父窗口类中为一个或一定范围特定的消息提供了一个处理函数,对于同样的消息,如果在您的处理中您并没有调用其基类的处理函数它就会覆盖掉被反射的消息处理。例如,如果你在一个对话框类中试图处理 WM_CTLCOLOR,您的处理将覆盖掉任何被反射的消息处理函数。

       如果你在父窗口中为一个或一系列一定范围的特定的 WM_NOTIFY消息提供一个处理函数,您的处理函数只有当这些发送消息的子控件通过ON_NOTIFY_REFLECT()宏就不会有一个被反射消息处理了。如果该处理返回 TRUE,消息就也会给父窗口来处理,而如果它返回的是一个 FALSE值,就不会让父窗口来处理该消息。请注意:被反射的消息是在通知消息之前被处理。

       当一个 WM_NOTIFY 消息被发送后,控件就得到了第一次的机会来处理它。如果任何其他的被反射消息被发送,父窗口会有第一个机会来处理之,而控件将能接收被反射的消息。为了达到这样的目的,在控件类消息映射中需要一个处理函数和一个合适的入口。

      反射消息的消息映射宏与通常通知的消息映射宏有点微小的区别:它需要在其常规名字中添加 _REFLECT。例如,为了在父窗口中处理WM_NOTIFY 消息,你可以在父窗口类的消息映射中使用宏 ON_NOTIFY。而在子控件类中处理反射消息,您必须使用ON_NOTIFY_REFLECT来响应消息。在一些情形之中,参数也是不一样的。请注意:ClassWizard(类向导)通常可以为你添加消息入口,并提供了一个带有正确参数的实现函数的大体框架。

       反射消息的消息映射入口和处理函数原型

       为了处理被反射过来的控件通知消息,使用列于下表的消息映射宏和函数原型。

       ClassWizard 通常能够为你加入消息映射入口并为您提供一个实现函数的骨架。请参见
《Defining a Message Handler for a Reflected Message in the Visual C++ Programmer's Guide》来获得有关怎么为反射消息定义处理函数的信息。

       为了把消息名称转化为被反射的宏名称,在消息名称前加 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。

       在上述特殊情形中,您必须指定处理成员函数的名字。而在其他情形中,您必须使用标准的处理函数名称。

       函数参数的意义和返回值的意思已经罗列于函数名称之下或已经预先写好了。例如,CtlColor 在文档中表示为 OnCtlColor。几个被反射消息处理函数需要的参数个数要比在其父窗口中相应的函数需要的参数要少。请参见下表中文档中的正式参数。

       映射宏 入口 函数原型

       ON_NOTIFY_REFLECT 和 ON_CONTROL_REFLECT 有几个变体,以允许诸如控件及其父窗口多个对象来处理给定的消息。

       映射宏 入口 函数原型

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

       这个简单的例子创建了一个可重用的控件,叫 CYellowEdit。该控件与一个常规编辑控件的功能几乎相同,不同就是它在黄色背景中显示黑字。当然,你可以很容易地添加一些成员函数使之显示不同的颜色。

       请参照下列步骤:

       在一个已存在的应用程序添加一个对话框,参见《dialog editor in the Visual C++ User's Guide》。
您必须有一个已存在的应用程序来开发可重用控件,如果您还没有可使用的应用程序,您不妨使用 AppWizard 来创建一个基于对话框的应用程序。

       等 VC++ 把您的应用程序载入之后,您使用 ClassWizard 创建一个基于 CEdit 类的新 CYellowEdit 类。取消 "Add To Component Gallery" 选项。

       在 CYellowEdit 类中添加三个成员变量。前两个是 COLORREF的变量以记录文本和背景颜色。第三个变量是一个用于绘制背景的刷子类 CBrush 变量。刷子变量允许你只创建一次,接下来只要引用它即可,一直到CYellowEdit 控件销毁的时候刷子才被自动销毁。

       在构造函数中初始化这些成员变量:
       CYellowEdit::CYellowEdit()
       {
       m_clrText = RGB( 0, 0, 0 );
       m_clrBkgnd = RGB( 255, 255, 0 );
       m_brBkgnd.CreateSolidBrush( m_clrBkgnd );
       }
       使用 ClassWizard 给您的 CYellowEdit 类为 WM_CTLCOLOR反射消息添加一个处理函数。注意,您能处理的消息列表中的消息名称前的等于号表明该消息是个可以被反射的消息。这在《Defining aMessage Handler for a Reflected Message in the Visual C++ Programmer'sGuide》中有所描述。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;
       }
       用下面的代码代替函数的主体。该程序代码很简单,就是指定文本颜色、文本背景颜色以及控件其余部分的背景颜色。

       pDC->SetTextColor( m_clrText ); // text
       pDC->SetBkColor( m_clrBkgnd ); // text bkgnd
       return m_brBkgnd; // ctl bkgnd
      在对话框中创建一个编辑控件,按下一个控件键下之后双击编辑控件并使之与一个成员变量相关联。在为对话框增添成员变量之际,完成变量名称,并在“控件”中选择“CYellowEdit”作为您的变量原型。别忘记在你的对话框中设置 Tab 顺序。另外也别忘记在你的对话框头文件中添加CYellowEdit 类的头文件。

       编译并运行您的程序,该编辑控件将显示一个黄色的背景。

       现在您可以使用 Component Gallery 把您的 CYellowEdit 控件类添加到其他工程项目中去。