TN062: Message Reflection for Windows Controls
本技術(shù)文檔解釋了消息反射,MFC 4.0的新特性,并指導(dǎo)讀者創(chuàng)建一個(gè)簡(jiǎn)單的、可重用的、使用了消息反射的控件。
本文并不討論適用于ActiveX控件(通常稱為OLE控件)的消息反射。請(qǐng)參看Visual C++ Programmer’s Guide的ActiveX Controls: Subclassing a Windows Control一文。
Windows控件頻繁地向其父窗口發(fā)送消息。例如,很多控件發(fā)送控件顏色提醒消息(WM_CTLCOLOR或其變種)給其父窗口,通知父窗口提供畫刷,以重畫控件的背景。
在4.0版本前的Windows和MFC中,父窗口,通常是一個(gè)對(duì)話框,負(fù)責(zé)處理這些消息。這意味著處理消息的代碼放置在父窗口類中,并且需要在任何需要處理這個(gè)消息的窗口中重復(fù)寫這些代碼。在這種情況下,每個(gè)對(duì)話框都必須為其內(nèi)部的控件定制背景色而處理提醒消息。若控件類可以自己處理其背景顏色,那么處理代碼的重用就會(huì)變得非常容易了。
在MFC 4.0中,舊的機(jī)制仍將正常運(yùn)行,即父窗口可以處理提醒消息。但同時(shí),MFC 4.0提供了消息反射,允許這些提醒消息既可在子控件中處理又可在其父窗口中處理,使得代碼重用更加容易。以控件背景顏色為例,現(xiàn)在可以創(chuàng)建一個(gè)控件,自己來處理WM_CTLCOLOR的反射消息,一切都不用依靠其父窗口。(注意,消息反射是由MFC實(shí)現(xiàn)的,而非Windows,因此要使用消息反射,父窗口必須繼承于CWnd。
舊版本的MFC為一些消息提供了一些虛函數(shù),使得消息的處理與消息反射機(jī)制相似。例如自畫列表框(owner-drawn list boxes)的消息(WM_DRAWITEM之類)。新的消息反射機(jī)制是通用并且一致的。
消息映射向后兼容MFC 4.0之前的版本。
若在父窗口提供了某一個(gè)或是一組指定消息的處理函數(shù),它將覆蓋反射的消息處理函數(shù)。在自己的消息處理函數(shù)中不應(yīng)調(diào)用父窗口的處理函數(shù)。例如: 若在對(duì)話框處理WM_CTLCOLOR消息,則此處理函數(shù)將會(huì)覆蓋所有反射消息的處理函數(shù)。
若在父窗口類中提供了某一個(gè)或一組WM_NOTIFY消息,則消息處理函數(shù)僅在子窗口沒有使用ON_NOTIFY_REFLECT()處理反射消息時(shí)被調(diào)用。若在消息映射中使用ON_NOTIFY_REFLECT_EX(),消息句柄可能會(huì)或可能不會(huì)允許父窗口處理這些消息。若處理函數(shù)返回TRUE,則父窗口也會(huì)處理此消息,而返回FALSE,則不允許父窗口處理。注意,反射消息在提醒消息前被處理。
當(dāng)WM_NOTIFY被發(fā)送時(shí),首先反射給控件來處理。而其他消息被發(fā)送時(shí),父窗口將首先處理,然后子控件才會(huì)接收到反射的消息。要讓子控件處理這些反射的消息,需要一個(gè)消息處理函數(shù),及消息映射實(shí)體。
反射消息的消息映射宏與普通的提醒消息稍有不同:它在普通的名字后加上了_REFLECT。例如,要在父窗口處理WM_NOTIFY消息,需要在父窗口的消息映射中加上ON_NOTIFY。而要在子控件里處理反射消息,則需要在子控件里使用ON_NOTIFY_REFLECT宏。在一些情況下,參數(shù)也有可能不同。注意,ClassWizard一般都可以正確地添加反射消息及其函數(shù)體。
參看TN061:ON_NOTIFY及WM_NOTIFY消息了解WM_NOTIFY消息。
要處理控件提醒消息的反射消息,使用下表列出的消息映射宏及函數(shù)原型。ClassWizard一般都可以正確地添加反射消息及其函數(shù)體。參看Visual C++ Programmer’s Guide的Defining a Message Handler for a Reflected Message,以了解如何定義反射消息的處理函數(shù)。
要將消息名轉(zhuǎn)化為反射宏名,在消息名前面加上ON_,并在其后面加上_REFLECT即可。例如:WM_CTLCOLOR的反射宏名為ON_WM_CTLCOLOR_REFLECT。(要查看哪些消息可以被反射,將下表的宏作逆變換。)
這種命名規(guī)則的特例有以下三個(gè):
l WM_COMMAND的反射宏為ON_CONTROL_REFLECT
l WM_NOTIFY的反射宏為ON_NITIFY_REFLECT
l ON_UPDATE_COMMAND_UI的反射宏為ON_UPDATE_COMMAND_UI_REFLECT
在上面三種特例下,必須指定處理函數(shù)名,而在其它情況下,必須使用標(biāo)準(zhǔn)的處理函數(shù)名。
函數(shù)參數(shù)及返回值的意義歸檔在其函數(shù)名,或函數(shù)名前加On下。例如CtlColor被歸檔在OnCtlColor中。一些反射消息的處理函數(shù)需要的參數(shù)比父窗口消息處理函數(shù)的更少。直接將本表中的名字與文檔中的形參名相對(duì)應(yīng)即可。
映射實(shí)體 | 函數(shù)原型 |
ON_CONTROL_REFLECT(wNotifyCode, memberFxn) | afx_msg void memberFxn(); |
ON_NOTIFY_REFLECT(wNotifyCode, memberFxn) | afx_msg void memberFxn(NMHDR * pNotifyStruct, LRESULT* result); |
ON_UPDATE_COMMAND_UI_REFLECT(memberFxn) | afx_msg void memberFxn(CCmdUI* pCmdUI); |
ON_WM_CTLCOLOR_REFLECT() | afx_msg HBRUSH CtlColor(CDC* pDC, UINT nCtlColor); |
ON_WM_DRAWITEM_REFLECT() | afx_msg void DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct); |
ON_WM_MEASUREITEM_REFLECT() | afx_msg void MeasureItem(LPMEASUREITEMSTRUCT lpMeasureItemStruct); |
ON_WM_DELETEITEM_REFLECT() | afx_msg void DeleteItem(LPDELETEITEMSTRUCT lpDeleteItemStruct); |
ON_WM_COMPAREITEM_REFLECT() | afx_msg int CompareItem(LPCOMPAREITEMSTRUCT lpCompareItemStruct); |
ON_WM_CHARTOITEM_REFLECT() | afx_msg int CharToItem(UINT nKey, UINT nIndex); |
ON_WM_VKEYTOITEM_REFLECT() | afx_msg int VKeyToItem(UINT nKey, UINT nIndex); |
ON_WM_HSCROLL_REFLECT() | afx_msg void HScroll(UINT nSBCode, UINT nPos); |
ON_WM_VSCROLL_REFLECT() | afx_msg void VScroll(UINT nSBCode, UINT nPos); |
ON_WM_PARENTNOTIFY_REFLECT() | afx_msg void ParentNotify(UINT message, LPARAM lParam); |
ON_NOTIFY_REFLECT及ON_CONTROL_REFLECT宏有以下變異體,允許其被多個(gè)對(duì)象處理(例如控件及其父窗口)。
映射實(shí)體 | 函數(shù)原型 |
ON_NOTIFY_REFLECT_EX(wNotifyCode, memberFxn) | afx_msg BOOL memberFxn(NMHDR * pNotifyStruct, LRESULT* result); |
ON_CONTROL_REFLECT_EX(wNotifyCode, memberFxn) | afx_msg BOOL memberFxn(); |
本例創(chuàng)建了一個(gè)可重用的控件CYellowEdit。此控件與普通的文本框相似,只是它在黃色背景上顯示黑色的文字。很容易為CYellowEdit添加成員函數(shù),以使之顯示不同的背景顏色。
實(shí)現(xiàn)步驟如下:
1. 在工程中創(chuàng)建一個(gè)新的對(duì)話框。請(qǐng)參見Visual C++ User’s Guide以了解更多信息。
為開發(fā)一個(gè)可重用的控件,應(yīng)有一個(gè)應(yīng)用程序。若沒有現(xiàn)成的應(yīng)用程序,使用AppWizard創(chuàng)建一個(gè)基于對(duì)話框的應(yīng)用程序。
2. 加載工程后,使用ClassWizard創(chuàng)建一個(gè)繼承于CEdit的新類CYellowEdit。選定“Add to Component Gallery”復(fù)選框。
3. 在CYellowEdit類里添加三個(gè)變量。前兩個(gè)變量為COLORREF類型,以存放文本顏色及背景顏色。第三個(gè)變量是一個(gè)CBrush對(duì)象,以存放背景畫刷。CBrush可以只創(chuàng)建一次,之后就直接引用,并且在CYellowEdit被銷毀的時(shí)候自動(dòng)銷毀。
4. 在構(gòu)造函數(shù)里初始化成員變量:
CYellowEdit::CYellowEdit()
{
m_clrText = RGB(0, 0, 0);
m_clrBkgnd = RGB(255, 255, 0);
m_brBkgnd.CreateSolidBrush(m_clrBkgnd);
}
使用ClassWizard,在CYellowEdit里添加一個(gè)WM_CTLCOLOR反射消息的處理函數(shù)。注意,消息名前的等號(hào)表示此消息已被反射。這在Visual C++ Programmer’s Guide的Defining a Message Handler for a Reflected Message有相應(yīng)描述。
ClassWizard將添加以下的消息映射宏及函數(shù):
ON_WM_CTLCOLOR_REFLECT()
// Note: other code will be in between….
HBRUSH CYellowEdit::CtlColor(CDC* pDC, UINT nCtlColor)
{
// TODO: Change any attributes of the DC here
// TODO: Return a non-NULL brush if the
// parent’s handler should not be called
return NULL;
}
用下面的代碼替換函數(shù)體。這些代碼指定了文字顏色,文字背景顏色以及控件其余部分的背景顏色。
pDC->SetTextColor(m_clrText); // text
pDC->SetBkColor(m_clrBkgnd); // text bkgnd
return m_brBkgnd; // ctl bkgnd
在對(duì)話框里創(chuàng)建一個(gè)文本框,按住CTRL鍵的同時(shí)雙擊文本框控件,以將其與一成員變量綁定。在Add Member Variable對(duì)話框中,輸入變量名,選擇”控件”,并選擇變量類型為CYellowEdit。不要忘了設(shè)置對(duì)話框的TAB順序。并且要在對(duì)話框的頭文件中包含CYellowEdit的頭文件。
編譯并運(yùn)行應(yīng)用程序,文本框?qū)⒂幸粋€(gè)黃色的背景。
現(xiàn)在可以使用Component Gallery將CYellowEdit控件類添加到其它工程中去了。
聯(lián)系客服