MFC框架由于古老,其与模板的结合比ATL/WTL差远了,其中一大原因就是因为宏的大量使用。最近封装MFC窗口类(CWnd)时,实在不想机械性地使用虚函数机制了,就想试试通过模板的手法实现代码的复用性。真的动起手来,其实发现也没那么麻烦。
对CWnd采用模板手法,最大的障碍就是宏的使用。由于CWnd中的宏都分为声明和定义2部分,声明在.h文件,定义在.cpp文件中。由于模板的具现化在编译器,最简单的解决就是将.cpp改成.inl格式,并在头文件末尾包含这个.inl文件。
类声明中的DECLARE_DYNAMIC和DECLARE_MESSAGE_MAP由于不需要模板参数可以暂且不管,类定义中对应的宏: IMPLEMENT_DYNAMIC和BEGIN_MESSAGE_MAP/ END_MESSAGE_MAP才是需要定制的关键。本想查看这些宏的定义以便自己实现对应的模板函数/变量,可当我看见BEGIN_TEMPLATE_MESSAGE_MAP宏时,才发现这个宏正是我想要的那种定制(可惜文档上查不到),省去一番功夫,以此类推, DECLARE_DYNAMIC宏肯定也有对应的模板形式,找了好久,勉勉强强找到了IMPLEMENT_DYNAMIC_T,但是这个宏虎头蛇尾的,根本就用不了。本想自己实现一个,可仔细想想这个宏的作用,才发现自己的模板类真的不需要。这个宏作用在乎给MFC类添加个静态变量,编制该类的类名,从而实在MFC的RTTI机制。但我的模板类只是封装代码,使其可复用,RTTI机制的支持完全可以放到具体的实现类中。
不知不觉中发现自己废话真有点多了,先上代码吧:
.h文件:
代码
template
<
typename T
>
class
CAuxWindowT :
public
CWnd
{
DECLARE_DYNAMIC(CAuxWindowT)
public
:
CAuxWindowT();
//
标准构造函数
virtual
~
CAuxWindowT();
public
:
BOOL CreateWnd(DWORD dwExStyle, LPCTSTR lpszWindowName, DWORD dwStyle,
const
RECT
&
rect, CWnd
*
pParentWnd, UINT nID, LPVOID lpParam
=
NULL);
protected
:
virtual
void
DoDataExchange(CDataExchange
*
pDX);
//
DDX/DDV 支持
DECLARE_MESSAGE_MAP()
int
OnCreate(LPCREATESTRUCT lpCreateStruct);
void
OnPaint();
BOOL OnEraseBkgnd(CDC
*
pDC);
};
#include
"
AuxWindow.inl
"
文件AuxWindow.inl的代码如下:
代码
#define
DECLARE_TEMPLATE_THIS_POINTER(type, var) \
type
*
var
=
static_cast
<
type
*>
(
this
); \
ATLASSERT(var
!=
NULL);
//
IMPLEMENT_DYNAMIC_T(CAuxWindowT, T, CWnd)
template
<
typename T
>
CAuxWindowT
<
T
>
::CAuxWindowT()
{
}
template
<
typename T
>
CAuxWindowT
<
T
>
::
~
CAuxWindowT()
{
}
template
<
typename T
>
BOOL CAuxWindowT
<
T
>
::CreateWnd(DWORD dwExStyle, LPCTSTR lpszWindowName, DWORD dwStyle,
const
RECT
&
rect, CWnd
*
pParentWnd, UINT nID, LPVOID lpParam)
{
DECLARE_TEMPLATE_THIS_POINTER(T, pThis);
CAuxInitWinStruct
<
WNDCLASSEX
>
wndClass;
wndClass.lpfnWndProc
=
::DefWindowProc;
wndClass.hInstance
=
AfxGetInstanceHandle();
wndClass.lpszClassName
=
pThis
->
GetRegisterClassName();
AUX_ASSERT_FAILED_RETURN_VALUE(AuxRegisterWindowClass(
&
wndClass), FALSE);
AUX_ASSERT_FAILED_RETURN_VALUE(
this
->
CreateEx(dwExStyle, pThis
->
GetRegisterClassName(), lpszWindowName, dwStyle, rect, pParentWnd, nID, lpParam), FALSE);
return
TRUE;
}
template
<
typename T
>
void
CAuxWindowT
<
T
>
::DoDataExchange(CDataExchange
*
pDX)
{
CWnd::DoDataExchange(pDX);
}
BEGIN_TEMPLATE_MESSAGE_MAP(CAuxWindowT, T, CWnd)
ON_WM_CREATE()
ON_WM_PAINT()
ON_WM_ERASEBKGND()
END_MESSAGE_MAP()
template
<
typename T
>
int
CAuxWindowT
<
T
>
::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
int
nRet
=
1
;
__if_exists(T::InsideCreate)
{
DECLARE_TEMPLATE_THIS_POINTER(T, pThis);
nRet
=
pThis
->
InsideCreate(lpCreateStruct);
}
return
nRet;
}
template
<
typename T
>
void
CAuxWindowT
<
T
>
::OnPaint()
{
__if_exists(T::InsidePaint)
{
DECLARE_TEMPLATE_THIS_POINTER(T, pThis);
pThis
->
InsidePaint();
}
__if_not_exists(T::InsidePaint)
{
CPaintDC dc(
this
);
}
}
template
<
typename T
>
BOOL CAuxWindowT
<
T
>
::OnEraseBkgnd(CDC
*
pDC)
{
__if_not_exists(T::InsideEraseBkgnd)
{
return
CWnd::OnEraseBkgnd(pDC);
}
DECLARE_TEMPLATE_THIS_POINTER(T, pThis);
return
pThis
->
InsideEraseBkgnd(pDC);
}
这种封装很大的问题在于CWnd的消息映射宏只能映射到自己类中的某个方法,故而我只有在封装中加了一层转发接口,在这里调用派生类的相应的方法(如果有的话).如果完全定制BEGIN_MESSAGE_MAP/ END_MESSAGE_MAP宏使其保存指向派生类的方法,可以实现,但改动实在较大(不亚于自己重头开始写一个窗口类了),而且代码复用的优点也削弱了不少。
派生类的定义:
代码
class
CFadeWindow :
public
CAuxWindowT
<
CFadeWindow
>
{
DECLARE_DYNAMIC(CFadeWindow)
public
:
CFadeWindow();
//
标准构造函数
virtual
~
CFadeWindow();
public
:
LPCTSTR GetRegisterClassName()
{
return
_T(
"
SinaShowFadeWindow
"
);
}
int
InsideCreate(LPCREATESTRUCT lpCreateStruct);
void
InsidePaint();
BOOL InsideEraseBkgnd(CDC
*
pDC);
};
// .cpp
IMPLEMENT_DYNAMIC(CFadeWindow, CAuxWindowT<CFadeWindow>)
代码注解:__if_exists和__if_not_exists用于判断某个标识符是否存在,其不能与else共用,所以提供2个相反的关键字。
CAuxInitWinStruct:用于初始化Windows结构体的封装
定义如下:
代码
//
//
封装Windows常用的结构体
//
自动进行清零操作和设置Windows结构体的cbSize字段
//
template
<
typename TBase
>
class
CAuxInitWinStruct :
public
TBase
{
public
:
CAuxInitWinStruct()
{
::ZeroMemory(
this
,
sizeof
(TBase));
this
->
cbSize
=
sizeof
(TBase);
}
CAuxInitWinStruct(
const
TBase
&
var)
{
memcpy(
this
,
&
var,
sizeof
(TBase));
}
};
AuxRegisterWindowClass:窗口类注册实现, 代码如下:
代码
//
注册窗口类
inline BOOL AuxRegisterWindowClass(
const
WNDCLASSEX
*
pWndClass)
{
ATLASSERT(pWndClass
!=
NULL);
BOOL bSuccess
=
TRUE;
//
先检测窗口是否注册过
WNDCLASSEX wndClassDummy;
BOOL bRegistered
=
::GetClassInfoEx(pWndClass
->
hInstance, pWndClass
->
lpszClassName,
&
wndClassDummy);
if
(
!
bRegistered
||
(wndClassDummy.lpfnWndProc
!=
pWndClass
->
lpfnWndProc))
{
//
如果未注册或者注册的窗口过程不是本地的函数地址, 则重新注册
if
(bRegistered)
{
//
如果已注册 则先取消注册
::UnregisterClass(wndClassDummy.lpszClassName, pWndClass
->
hInstance);
}
//
注册窗口
bSuccess
=
(::RegisterClassEx(pWndClass)
!=
0
)
?
TRUE : FALSE;
}
return
bSuccess;
}