TN061: ON_NOTIFY and WM_NOTIFY Messages
此技術(shù)文檔提供了WM_NOTIFY消息的背景信息,并描述了在MFC程序中處理WM_NOTIFY的推薦(并且是最普通)方法。
在Windows 3.x,控件通過發(fā)送消息來通知父窗口事件的發(fā)生,例如鼠標(biāo)點擊,內(nèi)容或選擇的改變,控件背景繪制等。簡單的提醒通過WM_COMMAND消息發(fā)送,消息的參數(shù)有提醒標(biāo)志(如BN_CLICKED),控件ID(作為wParam)及控件句柄(lParam)。注意,由于wParam和lParam已被占用,因此沒有辦法傳遞更多的參數(shù),這也是為什么只能通過這種方式發(fā)送簡單提醒。例如在BN_CLICKED中,沒有辦法傳遞按鍵被點擊時的鼠標(biāo)位置。
當(dāng)Windows 3.x里的控件在發(fā)送提醒消息時,需要包含額外的數(shù)據(jù),通常需要使用特定的消息,包括WM_CTLCOLOR、WM_VSCROLL、WM_HSCROLL、WM_DRAWITEM、WM_MEASUREITEM、WM_COMPAREITEM、WM_DELETEITEM、WM_CHARTOITEM、WM_VKEYTOITEM等。這些消息可以被反射回發(fā)送的控件。要了解更多信息,請參見TN062:Windows控件消息反射。
對于Windows 3.1中的控件,Win32 API使用大部分Windows 3.x中使用的提醒消息。然而Win32也增加了一些成熟的、復(fù)雜的控件來支持Windows 3.x。通常,這些控件在發(fā)送提醒消息的時候需要包含額外的數(shù)據(jù)。Win32 API的設(shè)計者選擇用一個消息,WM_NOTIFY,而不是為每一個提醒消息增加一個對應(yīng)的WM_*消息,以一種標(biāo)準的格式來傳遞任意數(shù)量的額外數(shù)據(jù)。
WM_NOTIFY消息將發(fā)送控件的ID作為其wParam,及一個結(jié)構(gòu)體指針作為lParam。這個結(jié)構(gòu)體要么是NMHDR,要么是更大的結(jié)構(gòu)體,并且其第一個元素是NMHDR類型。注意,由于NMHDR是第一個元素,因此某個指向此結(jié)構(gòu)體的指針也可以轉(zhuǎn)換為NMHDR指針使用。
在大多數(shù)情況下,此指針將指向一個更大的結(jié)構(gòu)體,在使用時需要強制類型轉(zhuǎn)換。僅在少數(shù)提醒消息中,例如通用提醒(消息名以NM_開始)及提示(tool tip)控件的TTN_SHOW和TTN_POP,使用NMHDR。
NMHDR結(jié)構(gòu)體包含發(fā)送此消息的控件的ID和句柄,以及提醒代碼(例如TTN_SHOW)。NMHDR的格式如下:
typedef struct tagNMHDR {
HWND hwndFrom;
UINT idFrom;
UINT code;
} NMHDR;
對于一個TTN_SHOW消息,成員code會被設(shè)置為TTN_SHOW。
大多數(shù)提醒消息傳遞一個更大的結(jié)構(gòu)體指針,并將NMHDR結(jié)構(gòu)體作為其第一個元素。例如,在列表視圖(list view)控件中按下鍵盤時發(fā)送的LVN_KEYDOWN消息,傳遞的結(jié)構(gòu)體為LV_KEYDOWN,定義如下:
typedef struct tagLV_KEYDOWN {
NMHDR hdr;
WORD wVKey;
UINT flags;
} LV_KEYDOWN;
注意,由于NMHDR是此結(jié)構(gòu)體的第一個成員,因此提醒消息傳遞的指針可以被轉(zhuǎn)化為NMHDR或是LV_KEYDOWN指針。
一些提醒消息對于所有的新控件是通用的,這些消息傳遞NMHDR結(jié)構(gòu)體指針。
提醒代碼 | 觸發(fā)條件 |
NM_CLICK | 用戶在控件中點擊鼠標(biāo)左鍵 |
NM_DBLCLK | 用戶在控件中雙擊鼠標(biāo)左鍵 |
NM_RCLICK | 用戶在控件中點擊鼠標(biāo)右鍵 |
NM_RDBLCLK | 用戶在控件中雙擊鼠標(biāo)右鍵 |
NM_RETURN | 控件獲得焦點情況下,用戶按下ENTER鍵 |
NM_SETFOCUS | 控件獲得焦點 |
NM_KILLFOCUS | 控件丟失焦點 |
NM_OUTOFMEMORY | 由于沒有足夠的內(nèi)存,控件不能完成某操作 |
CWnd::OnNotify函數(shù)處理提醒消息。其默認實現(xiàn)檢查消息映射以查詢可調(diào)用的處理函數(shù)。通常,程序員并不需要重寫OnNotify,而應(yīng)為自己的窗口類提供處理函數(shù)及其消息映射實體。
通過ClassWizard 屬性頁或WizardBar,ClassWizard可以創(chuàng)建ON_NOTIFY消息映射實體,并提供處理函數(shù)體。要了解更多使用ClassWizard的信息,請參看Visual C++ Programmer’s Guide的Mapping Messages to Functions。
ON_NOTIFY消息映射宏格式如下:
ON_NOTIFY(wNotifyCode, id, memberFxn)
wNotifyCode
要處理的提醒消息的代碼,如LVN_KEYDOWN。
id
發(fā)送提醒消息的控件ID
memberFxn
提醒消息的處理函數(shù)
處理函數(shù)的原型如下:
afx_msg void memberFxn(NMHDR * pNotifyStruct, LRESULT * result);
pNotifyStruct
上文中描述的參數(shù)結(jié)構(gòu)體
result
在函數(shù)返回前應(yīng)設(shè)置的返回代碼
例子
要使成員函數(shù)OnKeydownList1處理ID為IDC_LIST1的CListCtrl發(fā)送的LVN_KEYDOWN消息,可以用ClassWizard添加以下代碼到消息映射中:
ON_NOTIFY(LVN_KEYDOWN, IDC_LIST1, OnKeydownList1)
ClassWizard提供的函數(shù)體為:
void CMessageReflectionDlg::OnKeydownList1(NMHDR* pNMHDR, LRESULT* pResult)
{
LV_KEYDOWN* pLVKeyDow = (LV_KEYDOWN*)pNMHDR;
// TODO: Add your control notification handler
// code here
*pResult = 0;
}
注意,ClassWizard自動提供了參數(shù)的指針,可以直接使用pNMHDR或pLVKEYDOW來訪問提醒結(jié)構(gòu)體。
若需要為一組控件處理相同的WM_NOTIFY消息,可以使用ON_NOTIFY_RANGE來代替ON_NOTIFY。例如,可以讓一組按鈕為某一提醒消息執(zhí)行相同的操作。
當(dāng)使用ON_NOTIFY_RANGE,需要指定一組連續(xù)的控件ID,用于指定控件組的起始ID與結(jié)束ID。
ClassWizard并不會處理ON_NOTIFY_RANGE,要使用它,需要手動添加消息映射。
ON_NOTIFY_RANGE的消息映射實體及函數(shù)原因如下:
ON_NOTIFY_RANGE(wNotifyCode, id, idLast, memberFxn)
wNotifyCode
要處理的提醒消息的代碼,如LVN_KEYDOWN。
id
控件組的起始ID
idLast
控件組的結(jié)束ID
memberFxn
提醒消息的處理函數(shù)
處理函數(shù)的原型如下:
afx_msg void memberFxn(NMHDR * pNotifyStruct, LRESULT * result);
pNotifyStruct
上文中描述的參數(shù)結(jié)構(gòu)體
result
在函數(shù)返回前應(yīng)設(shè)置的返回代碼
若希望由多個對象處理提醒消息,可以使用ON_NOTIFY_EX(或ON_NOTIFY_EX_RANGE)來代替ON_NOTIFY(或ON_NOTIFY_RANGE)。EX版與常規(guī)版本的區(qū)別在于,EX版的處理函數(shù)有BOOL型的返回值,以決定消息的處理是否應(yīng)該繼續(xù)。若返回FALSE,則消息可以被多個對象處理。
ClassWizard并不處理ON_NOTIFY_EX或ON_NOTIFY_EX_RANGE;要使用它們,需要手動添加消息映射。
ON_NOTIFY_EX和ON_NOTIFY_EX_RANGE的消息映射實體及函數(shù)原型發(fā)下,參數(shù)的含義與常規(guī)版本一致:
ON_NOTIFY_EX(nCode, id, memberFxn)
ON_NOTIFY_EX_RANGE(wNotifyCode, id, idLast, memberFxn)
兩者的函數(shù)原型均為:
afx_msg BOOL memberFxn(UINT id, NMHDR * pNotifyStruct, LRESULT * result);
id表示發(fā)送提醒的控件的ID。
若提醒消息已被處理完,函數(shù)應(yīng)返回TRUE;否則,若需要進一步處理,函數(shù)返回FALSE。