Subclassing一個已有的Windows通用控制項,可以減少很多工作量。新的控制項可以繼承被subclass的控制項的很多能力,如繪製和對滑鼠的響應等。在用MFC ActiveX Control Wizard建立一個工程時,可以選擇Subclass a Windows Control,將產生一些必要的代碼。我們也可以手工向一個已有的ActiveX工程中加入這些代碼:
一、重載COleControl::IsSubclassedControl、PreCreateWindow
BOOL CDemoSubclassCtrl::PreCreateWindow(CREATESTRUCT &cs)
{
cs.lpszClass = _T("BUTTON") ; // 標識從一個BUTTON容器subclass
return COleControl::PreCreateWindow(cs) ;
}
BOOL CDemoSubclassCtrl::IsSubclassedControl()
{
return TRUE ;
}
二、修改OnDraw函數,不需要自己繪製了
void CDemoSubclassCtrl::OnDraw(CDC* pdc, const CRect& rcBounds, const CRect& rcInvalid)
{
DoSuperclassPaint(pdc, rcBounds);
}
三、處理Reflected容器訊息
Windows控制項一般都是通過向父視窗發訊息和從父視窗接受Reflected訊息來實現與父視窗通訊的。Reflect訊息主要目的在於讓控制項有機會控制自己的特性,如WM_CTLCOLOR、WM_DRAWITEM等。
但是ActiveX控制項與它的包容器之間的通訊機制並不一樣。ActiveX控制項通過向包容器發事件和讀取包容器的Ambient屬性來實現和包容器互動的。雖然ActiveX控制項也是容器,但它不需要向父視窗發任何訊息。事件表面上類似於訊息,但內部機制完全不一樣。事件的基礎是連接點,而MFC中的訊息處理靠的是一條從子類到父類的訊息鏈。(具體的可參閱侯捷的《深入淺出MFC》)
但是當一個ActiveX控制項subclass了一個Windows控制項後,它就會自動地向它的父視窗發訊息,如BN_CLICKED。要想辦法不讓它發訊息給包容器。為此,COleControl為這種類型的ActiveX控制項產生了一個額外的父視窗,名為"reflector",大小和位置與控制項一樣。當它接受到控制項發出的訊息後,它會再發一個reflector訊息返回給控制項,以告訴控制項它剛才發出了一個什麼樣的訊息,這樣控制項就能做出相應的處理了,比如再發出事件給包容器。
Message sent by the control |
Message reflected to the control |
WM_COMMAND |
OCM_COMMAND |
WM_CTLCOLORBTN |
OCM_CTLCOLORBTN |
WM_CTLCOLOREDIT |
OCM_CTLCOLOREDIT |
WM_CTLCOLORDLG |
OCM_CTLCOLORDLG |
WM_CTLCOLORLISTBOX |
OCM_CTLCOLORLISTBOX |
WM_CTLCOLORSCROLLBAR |
OCM_CTLCOLORSCROLLBAR |
WM_CTLCOLORSTATIC |
OCM_CTLCOLORSTATIC |
WM_CTLCOLOR |
OCM_CTLCOLOR |
WM_DRAWITEM |
OCM_DRAWITEM |
WM_MEASUREITEM |
OCM_MEASUREITEM |
WM_DELETEITEM |
OCM_DELETEITEM |
WM_VKEYTOITEM |
OCM_VKEYTOITEM |
WM_CHARTOITEM |
OCM_CHARTOITEM |
WM_COMPAREITEM |
OCM_COMPAREITEM |
WM_HSCROLL |
OCM_HSCROLL |
WM_VSCROLL |
OCM_VSCROLL |
WM_PARENTNOTIFY |
OCM_PARENTNOTIFY |
WM_NOTIFY |
OCM_NOTIFY |
需要手工加入對reflected message的處理:
(1)class CDemoSubclassCtrl : public COleControl{protected:LRESULT OnOcmCommand(WPARAM ,LPARAM) ;....} ;(2)BEGIN_MESSAGE_MAP(CDemoSubclassCtrl, COleControL)ON_MESSAGE(OCM_COMMAND, OnOcmCommand)END_MESSAGE_MAP()(3)LRESULT CDemoSubclassCtrl::OnOcmCommand(WPARAM wParam, LPARAM lParam){#ifdef _WIN32WORD wNotifyCode = HIWORD(wParam);#elseWORD wNotifyCode = HIWORD(lParam);#endifswitch (wNotifyCode){case BN_CLICKED:FireClick() ;// 一般是發事件給包容器。也可以做別的事情break;}return 0;}同樣的方法,也可以處理OCM_CTLCOLORBTN或OCM_DRAWITEM等訊息來改變按鈕的樣貌。