ATL-固有属性(CStockPropImpl)
2011-07-20 22:53 Clingingboy 阅读(1423) 评论(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