(聲明:原文來自MSDN 2001 Oct版,原文内涉及的連接配接由于是脫機連接配接,是以譯文内的連接配接是本人盡量找自MSDN online)
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儲存控件的句柄。此時注意,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: Message Reflection for Windows Controls. (TN062: Windows控件的消息反射)
Win32下的通告消息
對于那些Windows 3.1的控件, Win32 API使用那些曾在Windows3.x有的絕大部分通告消息。However, Win32 also adds a number of sophisticated, complex controls to those supported in Windows 3.x.這些控件經常發送帶附加資料的通告消息。設計者們沒有為每一個需要附加資料的通告消息增加一個新的WM_*消息,而是隻增加了一個消息,WM_NOTIFY,這個消息可以通過一标準化格式傳遞任意多的額外資料。
WM_NOTIFY消息包括 儲存發送消息控件ID的wParam和儲存一個結構指針的lParam兩部分。那個結構可以是一個NMHDR結構或者某些更大點的、以NMHDR結構為第一個成員的結構。這樣的話,一個指向該結構的指針可以是NMHDR結構指針,也可以是那個更大點的結構指針,看你怎麼轉換他了。
在大多數情況下,那個指針會指向更大點的結構,當你用到的時候就需要轉換它。 隻有幾個通告消息,如 common通告消息(名字以NM_開始),工具提示控件的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通告消息所使用的結構,在list view控件裡鍵盤按鍵被按下時發送這個消息。那個指針就指向LV_KEYDOWN結構體,定義如下:
typedef struct tagLV_KEYDOWN {
NMHDR hdr;
WORD wVKey;
UINT flags;
} LV_KEYDOWN;
這樣,因為NMHDR是這個結構的第一個成員,那個指針既可以轉換為NMHDR型指針也可以轉換為LV_KEYDOWN型指針。
(筆者注:标準命名約定,LV_KEYDOWN已經改名為:NMLVKEYDOWN。MSDN Library - October 2001上是這麼說的呵呵。)
對新Windows控件通用的通告消息
一些通告消息對所有新的Windows控件來說是通用的,這些通告消息傳遞一個指向NMHDR結構體的指針。
Notification code | Sent because |
NM_CLICK | 使用者在控件内單擊滑鼠左鍵 |
NM_DBLCLK | 使用者在控件内輕按兩下滑鼠左鍵 |
NM_RCLICK | 使用者在控件内單擊滑鼠右鍵 |
NM_RDBLCLK | 使用者在控件内輕按兩下滑鼠右鍵 |
NM_RETURN | 使用者在控件具有輸入焦點的時候按下回車 |
NM_SETFOCUS | 控件獲得輸入焦點 |
NM_KILLFOCUS | 控件失去輸入焦點 |
NM_OUTOFMEMORY | 控件因為沒有足夠的可用記憶體而不能完成一項操作 |
ON_NOTIFY: 在MFC應用程式裡處理 WM_NOTIFY 消息
函數CWnd::OnNotify處理通告消息。其預設實作是檢查通告消息處理函數的消息映射,然後調用。(checks the message map for notification handlers to call.)一般說來,你不用重載OnNotify。你可以寫一個處理函數,然後在你自己的視窗類的消息映射表裡添加一個該函數的消息映射入口。
ClassWizard,通過ClassWizard屬性頁或者WizardBar工具條,能夠建立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_msgvoidmemberFxn(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。例如,你有一組按鈕,想讓它們對某一通告消息執行相同的動作。
When you use ON_NOTIFY_RANGE, you specify a contiguous range of child identifiers for which to handle the notification message by specifying the beginning and ending child identifiers of the range.
(不太會翻譯,大意就是,使用ON_NOTIFY_RANGE的時候,要指定一個你所需要相同相同消息處理函數控件的ID範圍)
ClassWizard不去處理ON_NOTIFY_RANGE的使用,要用它,就自己在消息映射表裡編輯。
ON_NOTIFY_RANGE的消息映射入口與函數原型如下表示:
ON_NOTIFY_RANGE( wNotifyCode, id, idLast, memberFxn )
斜體字參數被替換為:
wNotifyCode
要被處理的通告消息代碼,如 LVN_KEYDOWN。
id
連續ID範圍裡的第一個。
idLast
連續ID範圍裡的最後一個。
memberFxn
通告消息發送後被調用的成員函數。
你的成員函數必須按照如下形式聲明:
afx_msgvoidmemberFxn(UINT id, NMHDR*pNotifyStruct,LRESULT*result);
斜體字參數為:
id
發送通告消息的控件ID。
pNotifyStruct
指向通告消息結構的指針,類型如上聲明。
result
指向函數傳回之前要被設定結果值的變量指針。
ON_NOTIFY_EX, ON_NOTIFY_EX_RANGE(這部分不了解,先放一放)
If you want more than one object in the notification routing to handle a message, you can useON_NOTIFY_EX (or ON_NOTIFY_EX_RANGE) rather than ON_NOTIFY (or ON_NOTIFY_RANGE). The only difference between the EX version and the regular version is that the member function called for the EX version returns a BOOL that indicates whether or not message processing should continue. Returning FALSE from this function allows you to process the same message in more than one object.
ClassWizard does not handle ON_NOTIFY_EX or ON_NOTIFY_EX_RANGE; if you want to use either of them, you need to edit your message map yourself.
The message-map entry and function prototype for ON_NOTIFY_EX and ON_NOTIFY_EX_RANGE are as follows. The meanings of the parameters are the same as for the non-EX versions.
ON_NOTIFY_EX(nCode,id,memberFxn)
ON_NOTIFY_EX_RANGE( wNotifyCode, id, idLast,memberFxn )
The prototype for both of the above is the same:
afx_msgBOOLmemberFxn(UINT id, NMHDR*pNotifyStruct,LRESULT*result);
In both cases, id holds the child identifier of the control that sent the notification.
Your function must return TRUE if the notification message has been completely handled orFALSE if other objects in the command routing should have a chance to handle the message.