深入剖析WTL框架(四)

 

superclass

superclass是一種生成新的窗口類的方法。它的中心思想是依靠現有的窗口類,克隆出另一個窗口類。被克隆的類可以是Windows預定義的窗口類,這些預定義的窗口類有按鈕或下拉框控制等等。也可以是一般的類。克隆的窗口類使用被克隆的類(基類)的窗口消息處理函數。

克隆類可以有自己的窗口消息處理函數,也可以使用基類的窗口處理函數。

需要注意的是,superclass是在註冊窗口類時就改變了窗口的行爲。即通過指定基類的窗口函數或是自己定義的窗口函數。這與後面講到的subclass是不同的。後者是在窗口創建完畢後,通過修改窗口函數的地址等改變一個窗口的行爲的。

請看示例(摘自MSDN):

class CBeepButton: public CWindowImpl< CBeepButton >
{
public:
   DECLARE_WND_SUPERCLASS( _T("BeepButton"), _T("Button") )
   BEGIN_MSG_MAP( CBeepButton )
      MESSAGE_HANDLER( WM_LBUTTONDOWN, OnLButtonDown )
   END_MSG_MAP()
   LRESULT OnLButtonDown( UINT, WPARAM, LPARAM, BOOL& bHandled )
   {
      MessageBeep( MB_ICONASTERISK );
      bHandled = FALSE; // alternatively: DefWindowProc()
      return 0;
   }
}; // CBeepButton

該類實現一個按鈕,在點擊它時,會有響聲。

該類的消息映射處理WM_LBUTTONDOWN消息。其它的消息由Windows缺省窗口函數處理。

在消息映射前面,有一個宏--DECLARE_WND_SUPERCLASS()。它的作用就是申明BeepButton是Button的一個superclass。

分析一下這個宏:

#define DECLARE_WND_SUPERCLASS(WndClassName, OrigWndClassName) \
static CWndClassInfo& GetWndClassInfo() \
{ \
	static CWndClassInfo wc = \
	{ \
		{ sizeof(WNDCLASSEX), 0, StartWindowProc, \
		  0, 0, NULL, NULL, NULL, NULL, NULL, WndClassName, NULL }, \
		OrigWndClassName, NULL, NULL, TRUE, 0, _T("") \
	}; \
	return wc; \
}

這個宏定義了一個靜態函數GetWndClassInfo()。這個函數返回了一個窗口類註冊時用到的數據結構CWndClassInfo。該結構的詳細定義如下:

struct _ATL_WNDCLASSINFOA
{
	WNDCLASSEXA m_wc;
	LPCSTR m_lpszOrigName;
	WNDPROC pWndProc;
	LPCSTR m_lpszCursorID;
	BOOL m_bSystemCursor;
	ATOM m_atom;
	CHAR m_szAutoName[13];
	ATOM Register(WNDPROC* p)
	{
		return AtlModuleRegisterWndClassInfoA(&_Module, this, p);
	}
};
struct _ATL_WNDCLASSINFOW
{
 … …
	{
		return AtlModuleRegisterWndClassInfoW(&_Module, this, p);
	}
};
typedef _ATL_WNDCLASSINFOA CWndClassInfoA;
typedef _ATL_WNDCLASSINFOW CWndClassInfoW;
#ifdef UNICODE
#define CWndClassInfo CWndClassInfoW
#else
#define CWndClassInfo CWndClassInfoA
#endif

這個結構調用了一個靜態函數AtlModuleRegisterWndClassInfoA(&_Module, this, p);。這個函數的用處就是註冊窗口類。

它指定了WndClassName是OrigWdClassName的superclass。

subclass

subclass是普遍採用的一種擴展窗口功能的方法。它的大致原理如下。

在一個窗口創建完了之後,將該窗口的窗口函數替換成新的窗口消息處理函數。這個新的窗口函數可以對某些需要處理的特定的消息進行處理,然後再將處理傳給原來的窗口函數。

注意它與superclass的區別。

Superclass是以一個類爲原版,進行克隆。既在註冊新的窗口類時,使用的是基類窗口的窗口函數。

而subclass是在某一個窗口註冊並創建後,通過修改該窗口的窗口消息函數的地址而實現的。它是針對窗口實例。

看一個從MSDN來的例子:

class CNoNumEdit: public CWindowImpl< CNoNumEdit >
{
   BEGIN_MSG_MAP( CNoNumEdit )
      MESSAGE_HANDLER( WM_CHAR, OnChar )
   END_MSG_MAP()
   LRESULT OnChar( UINT, WPARAM wParam, LPARAM, BOOL& bHandled )
   {
      TCHAR ch = wParam;
      if( _T('0') <= ch && ch <= _T('9') )
         MessageBeep( 0 );
      else
         bHandled = FALSE;
      return 0;
   }
};

這裏定義了一個只接收數字的編輯控件。即通過消息映射,定義了一個特殊的消息處理邏輯。

然後,我們使用CWindowImplT. SubclassWindow()來subclass一個編輯控件。

class CMyDialog: public CDialogImpl<CMyDialog>
{
public:
   enum { IDD = IDD_DIALOG1 };
   BEGIN_MSG_MAP( CMyDialog )
      MESSAGE_HANDLER( WM_INITDIALOG, OnInitDialog )
   END_MSG_MAP()
   LRESULT OnInitDialog( UINT, WPARAM, LPARAM, BOOL& )
   {
      ed.SubclassWindow( GetDlgItem( IDC_EDIT1 ) ); 
      return 0;
   }

   CNoNumEdit ed;
};

上述代碼中,ed.SubclassWindow( GetDlgItem( IDC_EDIT1 ) )語句是對IDC_EDIT1這個編輯控件進行subclass。該語句實際上是替換了編輯控件的窗口函數。

由於SubClassWindows()實現的機制和ATL封裝窗口函數的機制一樣,我們會在後面介紹ATL是怎麼實現它的。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章