zoukankan      html  css  js  c++  java
  • 采用模板的手法复用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;
        }
  • 相关阅读:
    iOS之App Store上架被拒Legal
    iOS之解决崩溃Collection <__NSArrayM: 0xb550c30> was mutated while being enumerated.
    iOS之延时执行(睡眠)的几种方法
    iOS之UILabel的自动换行
    iOS之开发中一些相关的路径以及获取路径的方法
    iOS之绘制虚线
    iOS之判断手机号码、邮箱格式是否正确
    iOS之计算上次日期距离现在多久, 如 xx 小时前、xx 分钟前等
    iOS之开发中常用的颜色及其对应的RGB值
    method invocation
  • 原文地址:https://www.cnblogs.com/fangkm/p/1876898.html
Copyright © 2011-2022 走看看