CSDN博客

img smallfool

MFC 技术注意第62条的翻译

发表于2001/11/20 9:38:00  833人阅读

技术注意062:Windows 控件的消息反射
TN062: Message Reflection for Windows Controls

本技术注意讲述了消息反射,一个 MFC4.0 中的新特色。同时讲述了使用消息反射建立一个简单可重用控件的指导。
This technical note describes message reflection, a new feature in MFC 4.0. It also contains directions for creating a simple reusable control that uses message reflection.

本文并没有就 ActiveX 控件(以前称为 OLE 控件)如何进行消息反射的。有关这方面的资料请参见《ActiveX Controls: Subclassing a Windows Control in Visual C++ Programmer's Guide》。
This technical note does not discuss message reflection as it applies to ActiveX controls (formerly called OLE controls). Please see the articleActiveX Controls: Subclassing a Windows Control in Visual C++ Programmer's Guide.

什么是消息反射?
What Is Message Reflection?

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

Windows controls frequently send notification messages to their parent windows. For instance, many controls send a control color notification message (WM_CTLCOLOR or one of its variants) to their parent to allow the parent to supply a brush for painting the background of the control.

在 Windows 和 MFC4.0 以前的版本中,父窗口——通常是一个对话框,会来响应处理这些消息。这就意味着处理这些消息的代码需要在父窗口类中实现,而且对于每一个类都要处理这些消息。在上述的情况下,每个需要自定义背景控件的对话框都不得不处理控件颜色通知消息。如果一个控件可以处理自己的背景颜色,而且可以被重用,事情将变的简单的多。
In Windows and in MFC prior to version 4.0, the parent window, often a dialog box, is responsible for handling these messages. This means that the code for handling the message needs to be in the parent window’s class and that it has to be duplicated in every class that needs to handle that message. In the case above, every dialog box that wanted controls with custom backgrounds would have to handle the control color notification message. It would be much easier to reuse code if a control class could be written that would handle its own background color.

在 MFC 4.0 中,旧的消息机制仍然在工作 —— 父窗口可以处理通知消息。另外同时,MFC 4.0 通过提供了一种称为“消息反射”的新机制使的重复使用变的容易多了。消息反射允许这些通知消息既能够被控件自身处理,也能够被其父窗口来处理。在上述控件背景颜色例子中,现在你就既可以通过被发射过来的WM_CTLCOLOR消息自己写一个能够处理背景颜色的控件类,所有的一切就不再依靠其父窗口了。(请注意,既然这个消息反射只能在 MFC 实现,不是 Windows 系统的。所以父窗口必须派生于CWnd,这样消息反射才能正常工作)。
In MFC 4.0, the old mechanism still works—parent windows can handle notification messages. In addition, however, MFC 4.0 facilitates reuse by providing a feature called “message reflection” that allows these notification messages to be handled in either the child control window or the parent window, or in both. In the control background color example, you can now write a control class that sets its own background color by handling the reflected WM_CTLCOLOR message—all without relying on the parent. (Note that since message reflection is implemented by MFC, not by Windows, the parent window class must be derived from CWnd for message reflection to work.)

MFC 旧版本通过为一些消息提供虚函数的机制部分实现了类似的功能,最典型的就是自绘制的列表框(WM_DRAWITEM等等)。然后新的消息反射机制更通用和持久。
Older versions of MFC did something similar to message reflection by providing virtual functions for a few messages, such as messages for owner-drawn list boxes (WM_DRAWITEM, and so on). The new message reflection mechanism is generalized and consistent.

消息消息机制向后与MFC4.0版本前的代码相兼容。
Message reflection is backward compatible with code written for versions of MFC previous to 4.0.
如果你在控件的父窗口类中为一个或一定范围特定的消息提供了一个处理函数,对于同样的消息,如果在您的处理中您并没有调用其基类的处理函数它就会覆盖掉被反射的消息处理。例如,如果你在一个对话框类中试图处理 WM_CTLCOLOR,您的处理将覆盖掉任何被反射的消息处理函数。
If you have supplied a handler for a specific message, or for a range of messages, in your parent window's class, it will override reflected message handlers for the same message provided you don't call the base class handler function in your own handler. For example, if you handle WM_CTLCOLOR in your dialog box class, your handling will override any reflected message handlers.

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

If, in your parent window class, you supply a handler for a specific WM_NOTIFY message or a range of WM_NOTIFY messages, your handler will be called only if the child control sending those messages does not have a reflected message handler through ON_NOTIFY_REFLECT(). If you use ON_NOTIFY_REFLECT_EX() in your message map, your message handler may or may not allow the parent window to handle the message. If the handler returns TRUE, the message will be handled by the parent as well, while a call that returns FALSE does not allow the parent to handle it. Note that the reflected message is handled before the notification message.

当一个 WM_NOTIFY 消息被发送后,控件就得到了第一次的机会来处理它。如果任何其他的被反射消息被发送,父窗口会有第一个机会来处理之,而控件将能接收被反射的消息。为了达到这样的目的,在控件类消息映射中需要一个处理函数和一个合适的入口。
When a WM_NOTIFY message is sent, the control is offered the first chance to handle it. If any other reflected message is sent, the parent window has the first chance to handle it and the control will receive the reflected message. To do so, it will need a handler function and an appropriate entry in the control's class message map.

反射消息的消息映射宏与通常通知的消息映射宏有点微小的区别:它需要在其常规名字中添加 _REFLECT。例如,为了在父窗口中处理 WM_NOTIFY 消息,你可以在父窗口类的消息映射中使用宏 ON_NOTIFY。而在子控件类中处理反射消息,您必须使用 ON_NOTIFY_REFLECT 来响应消息。在一些情形之中,参数也是不一样的。请注意:ClassWizard(类向导)通常可以为你添加消息入口,并提供了一个带有正确参数的实现函数的大体框架。
The message-map macro for reflected messages is slightly different than for regular notifications: it has _REFLECT appended to its usual name. For instance, to handle a WM_NOTIFY message in the parent, you use the macro ON_NOTIFY in the parent’s message map. To handle the reflected message in the child control, use the ON_NOTIFY_REFLECT macro in the child control’s message map. In some cases, the parameters are different, as well. Note that ClassWizard can usually add the message-map entries for you and provide skeleton function implementations with correct parameters.

请参阅 TN061: 在新 WM_NOTIFY 消息中的 ON_NOTIFY 与 WM_NOTIFY 信息
See TN061: ON_NOTIFY and WM_NOTIFY Messages for information on the new WM_NOTIFY message.


反射消息的消息映射入口和处理函数原型
Message-Map Entries and Handler Function Prototypes for Reflected Messages

为了处理被反射过来的控件通知消息,使用列于下表的消息映射宏和函数原型。
To handle a reflected control notification message, use the message-map macros and function prototypes listed in the table below.

ClassWizard 通常能够为你加入消息映射入口并为您提供一个实现函数的骨架。请参见
《Defining a Message Handler for a Reflected Message in the Visual C++ Programmer's Guide》来获得有关怎么为反射消息定义处理函数的信息。
ClassWizard can usually add these message-map entries for you and provide skeleton function implementations. SeeDefining a Message Handler for a Reflected Message in the Visual C++ Programmer's Guide for information about how to define handlers for reflected messages.

为了把消息名称转化为被反射的宏名称,在消息名称前加 ON_,然后在消息名称后添加
_REFLECT。例如,WM_CTLCOLOR 相应的就转变为 ON_WM_CTLCOLOR_REFLECT。
To convert from the message name to the reflected macro name, prepend ON_ and append _REFLECT. For example, WM_CTLCOLOR becomes ON_WM_CTLCOLOR_REFLECT. (To see which messages can be reflected, do the opposite conversion on the macro entries in the table below.)

上述规则[普遍通用,但是要注意有三个例外:
The three exceptions to the rule above are as follows:

用于 WM_COMMAND 通知的宏是 ON_CONTROL_REFLECT;
用于 WM_NOTIFY 通知的宏是 ON_NOTIFY_REFLECT;
用于 ON_UPDATE_COMMAND_UI 通知的宏是 ON_UPDATE_COMMAND_UI_REFLECT。

The macro for WM_COMMAND notifications is ON_CONTROL_REFLECT.
The macro for WM_NOTIFY reflections is ON_NOTIFY_REFLECT.
The macro for ON_UPDATE_COMMAND_UI reflections is ON_UPDATE_COMMAND_UI_REFLECT.

在上述特殊情形中,您必须指定处理成员函数的名字。而在其他情形中,您必须使用标准的处理函数名称。
In each of the above special cases, you must specify the name of the handler member function. I
n the other cases, you must use the standard name for your handler function.
函数参数的意义和返回值的意思已经罗列于函数名称之下或已经预先写好了。例如,CtlColor 在文档中表示为 OnCtlColor。几个被反射消息处理函数需要的参数个数要比在其父窗口中相应的函数需要的参数要少。请参见下表中文档中的正式参数。

The meanings of the parameters and return values of the functions are documented under either the function name or the function name with On prepended. For instance, CtlColor is documented in OnCtlColor. Several reflected message handlers need fewer parameters than the similar handlers in a parent window. Just match the names in the table below with the names of the formal parameters in the documentation.

映射宏    入口    函数原型
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 有几个变体,以允许诸如控件及其父窗口多个对象来处理给定的消息。

The ON_NOTIFY_REFLECT and ON_CONTROL_REFLECT macros have variations that allow more than one object (such as the control and its parent) to handle a given message.

映射宏    入口   函数原型
ON_NOTIFY_REFLECT_EX( wNotifyCode, memberFxn )  afx_msg BOOL memberFxn ( NMHDR * pNotifyStruct, LRESULT* result );
ON_CONTROL_REFLECT_EX( wNotifyCode, memberFxn ) afx_msg BOOL memberFxn ( );


处理被反射的消息:可重用控件的例子
Handling Reflected Messages: An Example of a Reusable control

这个简单的例子创建了一个可重用的控件,叫 CYellowEdit。该控件与一个常规编辑控件的功能几乎相同,不同就是它在黄色背景中显示黑字。当然,你可以很容易地添加一些成员函数使之显示不同的颜色。
This simple example creates a reusable control called CYellowEdit. The control works the same as a regular edit control except that it displays black text on a yellow background. It would be easy to add member functions that would allow the CYellowEdit control to display different colors.

1请参照下列步骤:
To try this example, do the following steps:

在一个已存在的应用程序添加一个对话框,参见《dialog editor in the Visual C++ User’s Guide》。

您必须有一个已存在的应用程序来开发可重用控件,如果您还没有可使用的应用程序,您不妨使用 AppWizard 来创建一个基于对话框的应用程序。
Create a new dialog box in an existing application. For more information seedialog editor in the Visual C++ User’s Guide.
You must have an application in which to develop the reusable control. If you don’t have an existing application to use, create a dialog-based application using AppWizard.

等 VC++ 把您的应用程序载入之后,您使用 ClassWizard 创建一个基于 CEdit 类的新CYellowEdit类。取消 "Add To Component Gallery" 选项。
With your project loaded into Visual C++, use ClassWizard to create a new class called CYellowEdit based on CEdit. Leave the “Add to Component Gallery” box checked.

在 CYellowEdit 类中添加三个成员变量。前两个是 COLORREF 的变量以记录文本和背景颜色。第三个变量是一个用于绘制背景的刷子类 CBrush 变量。刷子变量允许你只创建一次,接下来只要引用它即可,一直到 CYellowEdit 控件销毁的时候刷子才被自动销毁。
Add three member variables to your CYellowEdit class. The first two will be COLORREF variables to hold the text color and the background color. The third will be a CBrush object which will hold the brush for painting the background. The CBrush object allows you to create the brush once, merely referencing it after that, and to destroy the brush automatically when the CYellowEdit control is destroyed.

在构造函数中初始化这些成员变量:
Initialize the member variables by writing the constructor as follows:
CYellowEdit::CYellowEdit()
{
 m_clrText = RGB( 0, 0, 0 );
 m_clrBkgnd = RGB( 255, 255, 0 );
 m_brBkgnd.CreateSolidBrush( m_clrBkgnd );
}

使用 ClassWizard 给您的 CYellowEdit 类为 WM_CTLCOLOR 反射消息添加一个处理函数。注意,您能处理的消息列表中的消息名称前的等于号表明该消息是个可以被反射的消息。这在《Defining a Message Handler for a Reflected Message in the Visual C++ Programmer's Guide》中有所描述。ClassWizard 会为您添加下面的消息映射宏以及相应的函数骨架:

Using ClassWizard, add a handler for the reflected WM_CTLCOLOR message to your CYellowEdit class. Note that the equal sign in front of the message name in the list of messages you can handle indicates that the message is reflected. This is described inDefining a Message Handler for a Reflected Message in the Visual C++ Programmer's Guide.
ClassWizard adds the following message-map macro and skeleton function for you:

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;
}

用下面的代码代替函数的主体。该程序代码很简单,就是指定文本颜色、文本背景颜色以及控件其余部分的背景颜色。
Replace the body of the function with the following code. The code specifies the text color, the text background color, and the background color for rest of the control.
pDC->SetTextColor( m_clrText );    // text
pDC->SetBkColor( m_clrBkgnd );    // text bkgnd
return m_brBkgnd;                // ctl bkgnd

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

Create an edit control in your dialog box, then attach it to a member variable by double-clicking the edit control while holding a control key down. In the Add Member Variable dialog box, finish the variable name and choose “Control” for the category, then “CYellowEdit” for the variable type. Don’t forget to set the tab order in the dialog box. Also, be sure to include the header file for the CYellowEdit control in your dialog box’s header file.

编译并运行您的程序,该编辑控件将显示一个黄色的背景。
Build and run your application. The edit control will have a yellow background.

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

You can now use Component Gallery to add your CYellowEdit control class to other projects.

0 0

相关博文

我的热门文章

img
取 消
img