此技术文档提供了WM_NOTIFY消息的背景信息,并描述了在MFC程序中处理WM_NOTIFY的推荐(并且是最普通)方法。
Windows 3.x的提醒消息
在Windows 3.x,控件通过发送消息来通知父窗口事件的发生,例如鼠标点击,内容或选择的改变,控件背景绘制等。简单的提醒通过WM_COMMAND消息发送,消息的参数有提醒标志(如BN_CLICKED),控件ID(作为wParam)及控件句柄(lParam)。注意,由于wParam和lParam已被占用,因此没有办法传递更多的参数,这也是为什么只能通过这种方式发送简单提醒。例如在BN_CLICKED中,没有办法传递按键被点击时的鼠标位置。
当Windows 3.x里的控件在发送提醒消息时,需要包含额外的数据,通常需要使用特定的消息,包括WM_CTLCOLOR、WM_VSCROLL、WM_HSCROLL、WM_DRAWITEM、WM_MEASUREITEM、WM_COMPAREITEM、WM_DELETEITEM、WM_CHARTOITEM、WM_VKEYTOITEM等。这些消息可以被反射回发送的控件。要了解更多信息,请参见TN062:Windows控件消息反射。
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_SHOW和TTN_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 属性页或WizardBar,ClassWizard可以创建ON_NOTIFY消息映射实体,并提供处理函数体。要了解更多使用ClassWizard的信息,请参看Visual C++ Programmer's Guide的Mapping 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处理ID为IDC_LIST1的CListCtrl发送的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 = 0;
}
注意,ClassWizard自动提供了参数的指针,可以直接使用pNMHDR或pLVKEYDOW来访问提醒结构体。
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_EX,ON_NOTIFY_EX_RANGE
若希望由多个对象处理提醒消息,可以使用ON_NOTIFY_EX(或ON_NOTIFY_EX_RANGE)来代替ON_NOTIFY(或ON_NOTIFY_RANGE)。EX版与常规版本的区别在于,EX版的处理函数有BOOL型的返回值,以决定消息的处理是否应该继续。若返回FALSE,则消息可以被多个对象处理。
ClassWizard并不处理ON_NOTIFY_EX或ON_NOTIFY_EX_RANGE;要使用它们,需要手动添加消息映射。
ON_NOTIFY_EX和ON_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。