Subclassing a Windows Control

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 _WIN32
		WORD wNotifyCode = HIWORD(wParam);
	#else
		WORD wNotifyCode = HIWORD(lParam);
	#endif

		switch (wNotifyCode)
		{
		case BN_CLICKED:
			FireClick() ;	// 一般是发事件给包容器。也可以做别的事情
			break;
		}

		return 0;
	}

同样的方法,也可以处理OCM_CTLCOLORBTN或OCM_DRAWITEM等消息来改变按钮的样貌。
原文地址:https://www.cnblogs.com/lidabo/p/2741602.html