這個(gè)技術(shù)文章介紹了關(guān)于新WM_NOTIFY消息, 還描述了建議使用的一種在你的MFC應(yīng)用程序中處理WM_NOTIFY消息的方法。
在Windows 3.x下,控件通過發(fā)送一個(gè)消息給它的父窗口來告知諸如目標(biāo)點(diǎn)擊,內(nèi)容的變化與選中,控件北京繪制等等之類的事件。簡(jiǎn)單的通告消息以特殊的WM_COMMAND消息形式來發(fā)送,通知碼(如BN_CLICKED)與控件ID存放在wParam里,lParam保存控件的句柄。此時(shí)注意,wParam與lParam已經(jīng)裝滿了數(shù)據(jù),再也傳遞不了別的數(shù)據(jù)了,這些消息只能是簡(jiǎn)單的通告消息。舉個(gè)例子,BN_CLICKED通告消息,無法發(fā)送按下鼠標(biāo)按鍵時(shí)鼠標(biāo)的位置信息。
當(dāng)Windows 3.x下的控件需要發(fā)送包括額外數(shù)據(jù)的通告消息時(shí),它們使用各種特殊目的的消息,包括WM_CTLCOLOR, WM_VSCROLL, WM_HSCROLL, WM_DRAWITEM, WM_MEASUREITEM, WM_COMPAREITEM, WM_DELETEITEM, WM_CHARTOITEM, WM_VKEYTOITEM等等。這些消息能夠被反射回給發(fā)送它們的的控件,要看更多信息,查閱 TN062: Message Reflection for Windows Controls. (TN062: Windows控件的消息反射)
對(duì)于那些Windows 3.1的控件, Win32 API使用那些曾在Windows3.x有的絕大部分通告消息。However, Win32 also adds a number of sophisticated, complex controls to those supported in Windows 3.x.這些控件經(jīng)常發(fā)送帶附加數(shù)據(jù)的通告消息。設(shè)計(jì)者們沒有為每一個(gè)需要附加數(shù)據(jù)的通告消息增加一個(gè)新的WM_* 消息,而是只增加了一個(gè)消息,WM_NOTIFY,這個(gè)消息可以通過一標(biāo)準(zhǔn)化格式傳遞任意多的額外數(shù)據(jù)。
WM_NOTIFY消息包括 保存發(fā)送消息控件ID的wParam和保存一個(gè)結(jié)構(gòu)指針的lParam兩部分。那個(gè)結(jié)構(gòu)可以是一個(gè)NMHDR結(jié)構(gòu)或者某些更大點(diǎn)的、以NMHDR結(jié)構(gòu)為第一個(gè)成員的結(jié)構(gòu)。這樣的話,一個(gè)指向該結(jié)構(gòu)的指針可以是NMHDR結(jié)構(gòu)指針,也可以是那個(gè)更大點(diǎn)的結(jié)構(gòu)指針,看你怎么轉(zhuǎn)換他了。
在大多數(shù)情況下,那個(gè)指針會(huì)指向更大點(diǎn)的結(jié)構(gòu),當(dāng)你用到的時(shí)候就需要轉(zhuǎn)換它。 只有幾個(gè)通告消息,如 common通告消息(名字以NM_開始),工具提示控件的TTN_SHOW與TTN_POP,是實(shí)際上用到NMHDR結(jié)構(gòu)的。
那個(gè)NMHDR結(jié)構(gòu)或者為首成員的結(jié)構(gòu),包含發(fā)送消息的控件句柄和ID,還有通知碼(如TTN_SHOW)。NMHDR結(jié)構(gòu)格式如下:
typedef struct tagNMHDR {
HWND hwndFrom;
UINT idFrom;
UINT code;
} NMHDR;
對(duì)于TTN_SHOW消息,成員code應(yīng)被設(shè)置成TTN_SHOW。
大多數(shù)通告消息傳遞一個(gè)指向更大的把NMHDR結(jié)構(gòu)作為第一個(gè)成員的結(jié)構(gòu)的指針。舉個(gè)例子,看看list view控件的LVN_KEYDOWN通告消息所使用的結(jié)構(gòu),在list view控件里鍵盤按鍵被按下時(shí)發(fā)送這個(gè)消息。那個(gè)指針就指向LV_KEYDOWN結(jié)構(gòu)體,定義如下:
typedef struct tagLV_KEYDOWN {
NMHDR hdr;
WORD wVKey;
UINT flags;
} LV_KEYDOWN;
這樣,因?yàn)?strong>NMHDR是這個(gè)結(jié)構(gòu)的第一個(gè)成員,那個(gè)指針既可以轉(zhuǎn)換為NMHDR型指針也可以轉(zhuǎn)換為LV_KEYDOWN型指針。
(筆者注:標(biāo)準(zhǔn)命名約定,LV_KEYDOWN已經(jīng)改名為:NMLVKEYDOWN。MSDN Library - October 2001上是這么說的呵呵。)
一些通告消息對(duì)所有新的Windows控件來說是通用的,這些通告消息傳遞一個(gè)指向NMHDR結(jié)構(gòu)體的指針。
Notification code | Sent because |
NM_CLICK | 用戶在控件內(nèi)單擊鼠標(biāo)左鍵 |
NM_DBLCLK | 用戶在控件內(nèi)雙擊鼠標(biāo)左鍵 |
NM_RCLICK | 用戶在控件內(nèi)單擊鼠標(biāo)右鍵 |
NM_RDBLCLK | 用戶在控件內(nèi)雙擊鼠標(biāo)右鍵 |
NM_RETURN | 用戶在控件具有輸入焦點(diǎn)的時(shí)候按下回車 |
NM_SETFOCUS | 控件獲得輸入焦點(diǎn) |
NM_KILLFOCUS | 控件失去輸入焦點(diǎn) |
NM_OUTOFMEMORY | 控件因?yàn)闆]有足夠的可用內(nèi)存而不能完成一項(xiàng)操作 |
ON_NOTIFY: 在MFC應(yīng)用程序里處理 WM_NOTIFY 消息
函數(shù)CWnd::OnNotify處理通告消息。其默認(rèn)實(shí)現(xiàn)是檢查通告消息處理函數(shù)的消息映射,然后調(diào)用。(checks the message map for notification handlers to call.)一般說來,你不用重載OnNotify。你可以寫一個(gè)處理函數(shù),然后在你自己的窗口類的消息映射表里添加一個(gè)該函數(shù)的消息映射入口。
ClassWizard,通過ClassWizard屬性頁或者WizardBar工具條,能夠創(chuàng)建ON_NOTIFY消息映射入口,并且給您提供了一個(gè)處理函數(shù)的框架。更多關(guān)于通過ClassWizard使得添加消息映射更容易的信息,請(qǐng)看Visual C++ Programmer's Guide的 Mapping Messages to Functions 。
ON_NOTIFY 消息映射宏的語法如下:
ON_NOTIFY( wNotifyCode, id, memberFxn )
斜體字的參數(shù)被替換為:
wNotifyCode
要被處理的通告消息代碼,如 LVN_KEYDOWN。
id
發(fā)送通告消息的控件ID。
memberFxn
通告消息發(fā)送后被調(diào)用的成員函數(shù)。
你的成員函數(shù)必須按照如下形式聲明:
afx_msg void memberFxn( NMHDR * pNotifyStruct, LRESULT * result );
斜體字參數(shù)為::
pNotifyStruct
指向通告消息結(jié)構(gòu)的指針,類型如上聲明。
result
指向函數(shù)返回之前要被設(shè)置結(jié)果值的變量指針。
代碼實(shí)例
現(xiàn)指定你要成員函數(shù)OnKeydownList1函去處理ID為IDC_LIST1的CListCtrl控件的LVN_KEYDOWN消息,你可以通過ClassWizard把下面的內(nèi)容加入到你的消息映射表里:
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自動(dòng)生成合適類型的指針。你可以通過pNMHDR或者pLVKeyDow訪問通告消息結(jié)構(gòu)體。
如果你需要處理一組控件的同一個(gè)WM_NOTIFY消息,你可使用ON_NOTIFY_RANGE而不是ON_NOTIFY。例如,你有一組按鈕,想讓它們對(duì)某一通告消息執(zhí)行相同的動(dòng)作。
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.
(不太會(huì)翻譯,大意就是,使用ON_NOTIFY_RANGE的時(shí)候,要指定一個(gè)你所需要相同相同消息處理函數(shù)控件的ID范圍)
ClassWizard不去處理ON_NOTIFY_RANGE的使用,要用它,就自己在消息映射表里編輯。
ON_NOTIFY_RANGE的消息映射入口與函數(shù)原型如下表示:
ON_NOTIFY_RANGE( wNotifyCode, id, idLast, memberFxn )
斜體字參數(shù)被替換為:
wNotifyCode
要被處理的通告消息代碼,如 LVN_KEYDOWN。
id
連續(xù)ID范圍里的第一個(gè)。
idLast
連續(xù)ID范圍里的最后一個(gè)。
memberFxn
通告消息發(fā)送后被調(diào)用的成員函數(shù)。
你的成員函數(shù)必須按照如下形式聲明:
afx_msg void memberFxn( UINT id, NMHDR * pNotifyStruct, LRESULT * result );
斜體字參數(shù)為:
id
發(fā)送通告消息的控件ID。
pNotifyStruct
指向通告消息結(jié)構(gòu)的指針,類型如上聲明。
result
指向函數(shù)返回之前要被設(shè)置結(jié)果值的變量指針。
ON_NOTIFY_EX, ON_NOTIFY_EX_RANGE(這部分不理解,先放一放)
If you want more than one object in the notification routing to handle a message, you can use ON_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_msg BOOL memberFxn( 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 or FALSE if other objects in the command routing should have a chance to handle the message.
發(fā)表于 @ 2009年03月09日 20:48:00|評(píng)論(0 )|編輯|收藏
聯(lián)系客服