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机制的支持完全可以放到具体的实现类中。
不知不觉中发现自己废话真有点多了,先上代码吧:
代码
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的代码如下:
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
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宏使其保存指向派生类的方法,可以实现,但改动实在较大(不亚于自己重头开始写一个窗口类了),而且代码复用的优点也削弱了不少。
派生类的定义:代码
{
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;
}