代码改变世界

ATL-固有属性(CStockPropImpl)

2011-07-20 22:53  Clingingboy  阅读(1411)  评论(0编辑  收藏  举报

 

继承该类,派生类就可以支持一些固有属性

一实现方法

1.继承CStockPropImpl

class ATL_NO_VTABLE CBullsEye :
    public CStockPropImpl<CBullsEye, IBullsEye, &IID_IBullsEye,
                      &LIBID_ATLINTERNALSLib>,
    ...

2.虽然已经实现,如想对外开发访问属性,必须显示声明

interface IBullsEye : IDispatch {
    const int DISPID_ALTERNATECOLOR = 1;
    const int DISPID_BEEP = 2;
    const int DISPID_CENTERCOLOR = 3;
    const int DISPID_RINGCOUNT = 4;
    const int DISPID_RINGVALUE = 5;
    const int DISPID_APPLICATION = 6;
    const int DISPID_PARENT = 7;

    [propput, bindable, requestedit, id(DISPID_BACKCOLOR)]
    HRESULT BackColor([in]OLE_COLOR clr);
    [propget, bindable, requestedit, id(DISPID_BACKCOLOR)]
    HRESULT BackColor([out,retval]OLE_COLOR* pclr);
    [propput, bindable, requestedit, id(DISPID_BACKSTYLE)]
    HRESULT BackStyle([in]long style);
    [propget, bindable, requestedit, id(DISPID_BACKSTYLE)]
    HRESULT BackStyle([out,retval]long* pstyle);
    [propput, bindable, requestedit, id(DISPID_FORECOLOR)]
    HRESULT ForeColor([in]OLE_COLOR clr);
    [propget, bindable, requestedit, id(DISPID_FORECOLOR)]
    HRESULT ForeColor([out,retval]OLE_COLOR* pclr);
    [propput, bindable, requestedit, id(DISPID_ENABLED)]
    HRESULT Enabled([in]VARIANT_BOOL vbool);
    [propget, bindable, requestedit, id(DISPID_ENABLED)]
    HRESULT Enabled([out,retval]VARIANT_BOOL* pbool);
};

3.当属性发生变更时,可以有一个通知方法

void CBullsEye::OnForeColorChanged( )
{
…
} void CBullsEye::OnBackColorChanged( ) { …
} void CBullsEye::OnBackStyleChanged( ) { …
}

4.必须为支持的固有属性提供成员变量

public:
    OLE_COLOR                   m_clrBackColor;     // BackColor stock property
    OLE_COLOR                   m_clrForeColor;     // ForeColor stock property
    long                        m_nBorderStyle;     // BorderStyle stock property
    long                        m_nBorderWidth;     // BorderWidth stock property
    long                        m_nBackStyle;       // BackStyle stock property
    BOOL                        m_bEnabled;         // Enabled stock property

变量名可以参考深入解析ATL

二.内部实现

HRESULT STDMETHODCALLTYPE get_Picture(IPictureDisp** ppPicture)
{
    __if_exists(T::m_pPicture) 
    { 
        ATLTRACE(atlTraceControls,2,_T("CStockPropImpl::get_Picture\n"));
        ATLASSERT(ppPicture != NULL);
        if (ppPicture == NULL)
            return E_POINTER;

        T* pT = (T*) this;
        *ppPicture = pT->m_pPicture;
        if (*ppPicture != NULL)
            (*ppPicture)->AddRef();
    }
    return S_OK;
}

CStockPropImpl使用__if_exists来判断子类是否存在这个字段,缺点就是即使变量写错了,编译器也无法判断错误

附__if_exists && __if_not_exists用法

The __if_exists statement tests whether the specified identifier exists. If the identifier exists, the specified statement block is executed.

跟if语句不同,if必须是字段存在的且为1的情况下,__if_exists只是判断字段是否存在而已,并不在意是否为true或者false

// the__if_exists_statement.cpp
// compile with: /EHsc
#include <iostream>

template<typename T>
class X : public T {
public:
   void Dump() {
      std::cout << "In X<T>::Dump()" << std::endl;

      __if_exists(T::Dump) {
         T::Dump();
      }

      __if_not_exists(T::Dump) {
         std::cout << "T::Dump does not exist" << std::endl;
      }
   }   
};

class A {
public:
   void Dump() {
      std::cout << "In A::Dump()" << std::endl;
   }
};

class B {};

bool g_bFlag = true;

class C {
public:
   void f(int);
   void f(double);
};

int main() { 
   X<A> x1;
   X<B> x2;

   x1.Dump();
   x2.Dump();

   __if_exists(::g_bFlag) {
      std::cout << "g_bFlag = " << g_bFlag << std::endl;
   }
   

   __if_exists(C::f) {
      std::cout << "C::f exists" << std::endl;
   }

   return 0;
}

使用宏定义

IMPLEMENT_STOCKPROP(LONG, BackStyle, nBackStyle, DISPID_BACKSTYLE)
IMPLEMENT_STOCKPROP(LONG, BorderStyle, nBorderStyle, DISPID_BORDERSTYLE)
IMPLEMENT_STOCKPROP(LONG, BorderWidth, nBorderWidth, DISPID_BORDERWIDTH)

我们可以看下IMPLEMENT_STOCKPROP宏的实现

#define IMPLEMENT_STOCKPROP(type, fname, pname, dispid) \
    HRESULT STDMETHODCALLTYPE put_##fname(type pname) \
    { \
        __if_exists(T::m_##pname) \
        { \
            ATLTRACE(ATL::atlTraceControls,2,_T("CStockPropImpl::put_%s\n"), #fname); \
            T* pT = (T*) this; \
            if (pT->m_nFreezeEvents == 0 && pT->FireOnRequestEdit(dispid) == S_FALSE) \
                return S_FALSE; \
            pT->m_##pname = pname; \
            pT->m_bRequiresSave = TRUE; \
            if (pT->m_nFreezeEvents == 0) \
                pT->FireOnChanged(dispid); \
            __if_exists(T::On##fname##Changed) \
            { \
                pT->On##fname##Changed(); \
            } \
            pT->FireViewChange(); \
            pT->SendOnDataChange(NULL); \
        } \
        return S_OK; \
    } \
    HRESULT STDMETHODCALLTYPE get_##fname(type* p##pname) \
    { \
        __if_exists(T::m_##pname) \
        { \
            ATLTRACE(ATL::atlTraceControls,2,_T("CStockPropImpl::get_%s\n"), #fname); \
            ATLASSERT(p##pname != NULL); \
            if (p##pname == NULL) \
                return E_POINTER; \
            T* pT = (T*) this; \
            *p##pname = pT->m_##pname; \
        } \
        return S_OK; \
    }

其中还会判断是否定义了属性变更事件

三.自定义属性

1.先声明IDL文件

interface IBullsEye : IDispatch {
    const int DISPID_ALTERNATECOLOR = 1;
    const int DISPID_BEEP = 2;
    const int DISPID_CENTERCOLOR = 3;
    const int DISPID_RINGCOUNT = 4;
    const int DISPID_RINGVALUE = 5;
    const int DISPID_APPLICATION = 6;
    const int DISPID_PARENT = 7;

       [propget, bindable, requestedit, id(DISPID_CENTERCOLOR), helpstring("property CenterColor")]
    HRESULT CenterColor([out, retval] OLE_COLOR* pVal);
    [propput, bindable, requestedit, id(DISPID_CENTERCOLOR), helpstring("property CenterColor")]
    HRESULT CenterColor([in] OLE_COLOR newVal);

    };

2.手动实现get,put方法

STDMETHODIMP CBullsEye::get_CenterColor(OLE_COLOR *pVal)
{
    if( pVal == 0 ) return E_POINTER;

    *pVal = m_clrCenterColor;
    return S_OK;
}

STDMETHODIMP CBullsEye::put_CenterColor(OLE_COLOR newVal)
{
    if( m_clrCenterColor == newVal ) return S_OK;

    if(!m_nFreezeEvents)
    {
        if( FireOnRequestEdit(DISPID_CENTERCOLOR) == S_FALSE )
        {
            return S_FALSE;
        }
    }

    m_clrCenterColor = newVal;             // Save new color
    ::DeleteObject(m_centerBrush);         // Clear old brush color
    m_centerBrush = 0;

    m_bRequiresSave = TRUE;                // Set dirty flag
    if( !m_nFreezeEvents )
    {
        FireOnChanged(DISPID_CENTERCOLOR); // Notify container of change
    }
    FireViewChange( );                     // Request redraw
    SendOnDataChange(0);                   // Notify advise sinks of change
    return S_OK;
}

get,put实现几乎与固有属性实现一致,所以也可以直接用宏IMPLEMENT_STOCKPROP在头文件中实现

3.属性与控件呈现

有些属性改变需要重绘UI,那么就需要调用FireViewChange方法

4.关于FireOnChanged、FireOnRequestEdit、FireViewChange等方法的实现

CComControlBase和CComControl提供了这些辅助方法来实现,所以子类还需要继承CComControl