采用模板的手法复用CWnd类

      MFC框架由于古老,其与模板的结合比ATL/WTL差远了,其中一大原因就是因为宏的大量使用。最近封装MFC窗口类(CWnd),实在不想机械性地使用虚函数机制了,就想试试通过模板的手法实现代码的复用性。真的动起手来,其实发现也没那么麻烦。

     CWnd采用模板手法,最大的障碍就是宏的使用。由于CWnd中的宏都分为声明和定义2部分,声明在.h文件,定义在.cpp文件中。由于模板的具现化在编译器,最简单的解决就是将.cpp改成.inl格式,并在头文件末尾包含这个.inl文件。

类声明中的DECLARE_DYNAMICDECLARE_MESSAGE_MAP由于不需要模板参数可以暂且不管,类定义中对应的宏: IMPLEMENT_DYNAMICBEGIN_MESSAGE_MAP/ END_MESSAGE_MAP才是需要定制的关键。本想查看这些宏的定义以便自己实现对应的模板函数/变量,可当我看见BEGIN_TEMPLATE_MESSAGE_MAP宏时,才发现这个宏正是我想要的那种定制(可惜文档上查不到),省去一番功夫,以此类推, DECLARE_DYNAMIC宏肯定也有对应的模板形式,找了好久,勉勉强强找到了IMPLEMENT_DYNAMIC_T,但是这个宏虎头蛇尾的,根本就用不了。本想自己实现一个,可仔细想想这个宏的作用,才发现自己的模板类真的不需要。这个宏作用在乎给MFC类添加个静态变量,编制该类的类名,从而实在MFCRTTI机制。但我的模板类只是封装代码,使其可复用,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(thissizeof(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;
    }
原文地址:https://www.cnblogs.com/fangkm/p/1876898.html