Rover12421's Blog

The End.

[翻译,MSDN]TN061: ON_NOTIFY and WM_NOTIFY Messages

TN061ON_NOTIFYWM_NOTIFY消息

TN061: ON_NOTIFY and WM_NOTIFY Messages

此技术文档提供了WM_NOTIFY消息的背景信息,并描述了在MFC程序中处理WM_NOTIFY的推荐(并且是最普通)方法。

Windows 3.x的提醒消息

Windows 3.x,控件通过发送消息来通知父窗口事件的发生,例如鼠标点击,内容或选择的改变,控件背景绘制等。简单的提醒通过WM_COMMAND消息发送,消息的参数有提醒标志(如BN_CLICKED),控件ID(作为wParam)及控件句柄(lParam)。注意,由于wParamlParam已被占用,因此没有办法传递更多的参数,这也是为什么只能通过这种方式发送简单提醒。例如在BN_CLICKED中,没有办法传递按键被点击时的鼠标位置。

Windows 3.x里的控件在发送提醒消息时,需要包含额外的数据,通常需要使用特定的消息,包括WM_CTLCOLORWM_VSCROLLWM_HSCROLLWM_DRAWITEMWM_MEASUREITEMWM_COMPAREITEMWM_DELETEITEMWM_CHARTOITEMWM_VKEYTOITEM等。这些消息可以被反射回发送的控件。要了解更多信息,请参见TN062Windows控件消息反射

Win32的提醒消息

对于Windows 3.1中的控件,Win32 API使用大部分Windows 3.x中使用的提醒消息。然而Win32也增加了一些成熟的、复杂的控件来支持Windows 3.x。通常,这些控件在发送提醒消息的时候需要包含额外的数据。Win32 API的设计者选择用一个消息,WM_NOTIFY,而不是为每一个提醒消息增加一个对应的WM_*消息,以一种标准的格式来传递任意数量的额外数据。

WM_NOTIFY消息将发送控件的ID作为其wParam,及一个结构体指针作为lParam。这个结构体要么是NMHDR,要么是更大的结构体,并且其第一个元素是NMHDR类型。注意,由于NMHDR是第一个元素,因此某个指向此结构体的指针也可以转换为NMHDR指针使用。

在大多数情况下,此指针将指向一个更大的结构体,在使用时需要强制类型转换。仅在少数提醒消息中,例如通用提醒(消息名以NM_开始)及提示(tool tip)控件的TTN_SHOWTTN_POP,使用NMHDR

NMHDR结构体包含发送此消息的控件的ID和句柄,以及提醒代码(例如TTN_SHOW)。NMHDR的格式如下:

typedef struct tagNMHDR {

    HWND hwndFrom;

    UINT idFrom;

    UINT code;

} NMHDR;

对于一个TTN_SHOW消息,成员code会被设置为TTN_SHOW

大多数提醒消息传递一个更大的结构体指针,并将NMHDR结构体作为其第一个元素。例如,在列表视图(list view)控件中按下键盘时发送的LVN_KEYDOWN消息,传递的结构体为LV_KEYDOWN,定义如下:

typedef struct tagLV_KEYDOWN {

    NMHDR hdr;  

    WORD wVKey; 

    UINT flags; 

} LV_KEYDOWN;

注意,由于NMHDR是此结构体的第一个成员,因此提醒消息传递的指针可以被转化为NMHDR或是LV_KEYDOWN指针。

Windows新控件的通用提醒消息

一些提醒消息对于所有的新控件是通用的,这些消息传递NMHDR结构体指针。

提醒代码

触发条件

NM_CLICK

用户在控件中点击鼠标左键

NM_DBLCLK

用户在控件中双击鼠标左键

NM_RCLICK

用户在控件中点击鼠标右键

NM_RDBLCLK

用户在控件中双击鼠标右键

NM_RETURN

控件获得焦点情况下,用户按下ENTER

NM_SETFOCUS

控件获得焦点

NM_KILLFOCUS

控件丢失焦点

NM_OUTOFMEMORY

由于没有足够的内存,控件不能完成某操作

ON_NOTIFY:在MFC应用程序里处理WM_NOTIFY消息

CWnd::OnNotify函数处理提醒消息。其默认实现检查消息映射以查询可调用的处理函数。通常,程序员并不需要重写OnNotify,而应为自己的窗口类提供处理函数及其消息映射实体。

通过ClassWizard 属性页或WizardBarClassWizard可以创建ON_NOTIFY消息映射实体,并提供处理函数体。要了解更多使用ClassWizard的信息,请参看Visual C++ Programmer's GuideMapping Messages to Functions

ON_NOTIFY消息映射宏格式如下:

ON_NOTIFY(wNotifyCode, id, memberFxn)

wNotifyCode

要处理的提醒消息的代码,如LVN_KEYDOWN

id

发送提醒消息的控件ID

memberFxn

提醒消息的处理函数

处理函数的原型如下:

afx_msg void memberFxn(NMHDR * pNotifyStruct, LRESULT * result);

pNotifyStruct

上文中描述的参数结构体

result

在函数返回前应设置的返回代码

例子

要使成员函数OnKeydownList1处理IDIDC_LIST1CListCtrl发送的LVN_KEYDOWN消息,可以用ClassWizard添加以下代码到消息映射中:

ON_NOTIFY(LVN_KEYDOWN, IDC_LIST1, OnKeydownList1)

ClassWizard提供的函数体为:

void CMessageReflectionDlg::OnKeydownList1(NMHDR* pNMHDR, LRESULT* pResult)

{

    LV_KEYDOWN* pLVKeyDow = (LV_KEYDOWN*)pNMHDR;

    // TODO: Add your control notification handler

    //       code here

   

    *pResult = ;

}

注意,ClassWizard自动提供了参数的指针,可以直接使用pNMHDRpLVKEYDOW来访问提醒结构体。

ON_NOTIFY_RANGE

若需要为一组控件处理相同的WM_NOTIFY消息,可以使用ON_NOTIFY_RANGE来代替ON_NOTIFY。例如,可以让一组按钮为某一提醒消息执行相同的操作。

当使用ON_NOTIFY_RANGE,需要指定一组连续的控件ID,用于指定控件组的起始ID与结束ID

ClassWizard并不会处理ON_NOTIFY_RANGE,要使用它,需要手动添加消息映射。

ON_NOTIFY_RANGE的消息映射实体及函数原因如下:

ON_NOTIFY_RANGE(wNotifyCode, id, idLast, memberFxn)

wNotifyCode

要处理的提醒消息的代码,如LVN_KEYDOWN

id

控件组的起始ID

idLast

控件组的结束ID

memberFxn

提醒消息的处理函数

处理函数的原型如下:

afx_msg void memberFxn(NMHDR * pNotifyStruct, LRESULT * result);

pNotifyStruct

上文中描述的参数结构体

result

在函数返回前应设置的返回代码

ON_NOTIFY_EXON_NOTIFY_EX_RANGE

若希望由多个对象处理提醒消息,可以使用ON_NOTIFY_EX(或ON_NOTIFY_EX_RANGE)来代替ON_NOTIFY(或ON_NOTIFY_RANGE)。EX版与常规版本的区别在于,EX版的处理函数有BOOL型的返回值,以决定消息的处理是否应该继续。若返回FALSE,则消息可以被多个对象处理。

ClassWizard并不处理ON_NOTIFY_EXON_NOTIFY_EX_RANGE;要使用它们,需要手动添加消息映射。

ON_NOTIFY_EXON_NOTIFY_EX_RANGE的消息映射实体及函数原型发下,参数的含义与常规版本一致:

ON_NOTIFY_EX(nCode, id, memberFxn)

ON_NOTIFY_EX_RANGE(wNotifyCode, id, idLast, memberFxn)

两者的函数原型均为:

afx_msg BOOL memberFxn(UINT id, NMHDR * pNotifyStruct, LRESULT * result);

id表示发送提醒的控件的ID

若提醒消息已被处理完,函数应返回TRUE;否则,若需要进一步处理,函数返回FALSE

Comments