ActiveX控件的MFC设计之旅-第11步 .

ActiveX控件的MFC设计之旅-第11步 .,第1张

概述 OLE自动化集合类 在VB中有下面的这种语法 Set docs = Application.DocumentsFor Each doc in docs  MsgBox doc.TitleNext 在这里docs就是一个集合类,VB中还专门提供了一个Collection对象,可以组建你自己的集合类 当然,集合类还有许多特征,一个集合类都会有下面的几个方法或属性 Count只读属性 It olE自动化集合类

在VB中有下面的这种语法
Set docs = Application.documentsFor Each doc in docs MsgBox doc.TitleNext
在这里docs就是一个集合类,VB中还专门提供了一个Collection对象,可以组建你自己的集合类
当然,集合类还有许多特征,一个集合类都会有下面的几个方法或属性
Count只读属性
Item方法,可以带1个或多个参数,返回集合内的对象,一般设置为集合类的缺省属性(方法)
_NewEnum只读属性,这个属性对用户不可见,主要就用于上面的for each语法
一般来说,可能还会有Add和Remove等方法

下面,我们就将一步步来实现这个一个集合类Tcoll

前面的步骤都不说了

1.新建一个派生自CCmdTarget,选上Createable by type ID选项的CTcItem类,添加BSTR Key和BSTR Text属性,作为我们的集合中的元素,其中属性Key为关键字,用于利用关键字来选择集合元素,属性Text即为元素的文本,用于在控件在显示,这是个 很简单的类
另外,我们为CTcItem添加一个控件的指针CTcollCtrl* m_pCtrl;作用是为了在Text属性改变时,能够刷新控件
class CTcollCtrl;
class CTcItem : public CCmdTarget
{
...
public:
CTcollCtrl* m_pCtrl;
CString m_key;
CString m_text;
...
}
m_key和m_text用向导生成时,是protected的,但是,这里将它们移到了public下,因为后面会用到。

CTcItem::CTcItem()
{
Enableautomation();

// To keep the application running as long as an olE automation
// object is active,the constructor calls AfxoleLockApp.
m_pCtrl = NulL;
AfxoleLockApp();
}

voID CTcItem::OnTextChanged()
{
// Todo: Add notification handler code
if(m_pCtrl)
m_pCtrl->InvalIDateControl();
}

voID CTcItem::OnKeyChanged()
{
// Todo: Add notification handler code

}

2.新建一个派生自CCmdTarget,选上automation选项的CColl类,作为我们的集合类,添加long Count只读属性,LPdisPATCH Item(VARIANT Index)方法,LPUNKNowN NewEnum属性,LPdisPATCH Add(VARIATN strKey,VARIANT strText)方法和voID Remove(VARIANT Index)方法

对于Item方法,需要设置为缺省属性,可是似乎向导对于方法,并不提供Default Property这一选项,所以我们得自己加上了,下面的disP_DEFVALUE宏就是用于设置缺省属性的,另外得将Item的dispID改为0
NewEnum是很特殊的,因为,它的名字并不重要,你可以改成其它的阿狗阿猫,但是它的dispID必须为disPID_NEWENUM=-4
所以上面的这些更改都在两个地方完成,一是在集合类的.cpp中,另一个是在.odl中,如下:

Coll.cpp:
BEGIN_disPATCH_MAP(CColl,CCmdTarget)
//{{AFX_disPATCH_MAP(CColl)
disP_PROPERTY_EX(CColl,"Count",GetCount,SetNotSupported,VT_I4)
disP_FUNCTION(CColl,"Item",Item,VT_disPATCH,VTS_VARIANT)
disP_DEFVALUE(CColl,"Item")
disP_PROPERTY_EX_ID(CColl,"NewEnum",disPID_NEWENUM,_NewEnum,VT_UNKNowN)
disP_FUNCTION(CColl,"Remove",Remove,VT_EMPTY,VTS_VARIANT)
disP_FUNCTION(CColl,"Add",Add,VTS_VARIANT VTS_VARIANT)
//}}AFX_disPATCH_MAP
END_disPATCH_MAP()

Tcoll.odl:
// Primary dispatch interface for CColl
#define disPID_NEWENUM -4

[ uuID(0D58DCBE-EBDF-4746-80FD-8F389CF6BB0E) ]
dispinterface IColl
{
propertIEs:
[ID(disPID_NEWENUM)] IUnkNown* _NewEnum;
// NOTE - ClassWizard will maintain property information here.
// Use extreme caution when editing this section.
//{{AFX_ODL_PROP(CColl)
[ID(1)] long Count;
//}}AFX_ODL_PROP

methods:
[ID(0)] ITcItem* Item(VARIANT Index);
// NOTE - ClassWizard will maintain method information here.
// Use extreme caution when editing this section.
//{{AFX_ODL_METHOD(CColl)
[ID(3)] voID Remove(VARIANT Index);
[ID(4)] ITcItem* Add([optional]VARIANT strKey,[optional]VARIANT strText);
//}}AFX_ODL_METHOD

};

注:
a.这里将_NewEnum和Item都提到了向导生成代码的外面,是为了避免新增加属性或方法时向导乱改dispID,而且,很显然,向导也不认识disPID_NEWENUM之类的dispID。
b.这里的Item和Add的返回值都从Idispatch*改成了ITcItem*,这是为了能在VB设计代码时显示出接口的属性或方法。
c.这里将Add方法的两个参数均加上了optional选项,是为了可以使用缺省参数,就象ListVIEw控件的ListItems对象的Add方法 ListVIEw1.ListItems.Add,"Hello"一样的使用,要使用缺省参数的话,就一定要用VARIANT,所以这里用了VARIANT,而不是BSTR
d.这里的Item和Remove中的索引都是用的VARIANT,因为我们有可能会用关键字来选择集合中的元素。
e.将元素添加到集合中的Add方法是由你自己定的,这里是由函数来生成一个元素,你也可以直接添加一个Idispatch(ITcItem)元素到集合 中(在用CCmdTarget派生时,需要设置createable by type ID选项),下面的注释掉的代码中有这种方法的实现,仅供参考。

根据上面的描述,不难写出下面的实现代码

long CColl::GetCount()
{
// Todo: Add your property handler here
return m_olitems.GetCount();
return 0;
}

LPdisPATCH CColl::Item(const VARIANT FAR& Index)
{
// Todo: Add your dispatch handler code here
if(Index.vt == VT_I4){
position pos = m_olitems.Findindex(Index.lVal);
if(pos){
CTcItem* pitem = (CTcItem*)m_olitems.GetAt(pos);
if(pitem != NulL){
return pitem->GetIdispatch(TRUE);
}
}
return NulL;
}
else if(Index.vt == VT_BSTR){
position pos = m_olitems.Getheadposition();
while(pos){
CTcItem* pitem = (CTcItem*)m_olitems.GetNext(pos);
if(pitem->m_key == Index.bstrVal){
return pitem->GetIdispatch(TRUE);
}
}
return NulL;
}
return NulL;
}

LPUNKNowN CColl::_NewEnum()
{
// Todo: Add your property handler here
m_xEnumVARIANT.m_posCurrent = m_olitems.Getheadposition();
LPUNKNowN pUnk = GetInterface(&IID_IEnumVARIANT);
if(pUnk) pUnk->AddRef();
return pUnk;
return NulL;
}

LPdisPATCH CColl::Add(const VARIANT FAR& strKey,const VARIANT FAR& strText)
{
// Todo: Add your dispatch handler code here
CTcItem* pitem = new CTcItem;
pitem->m_pCtrl = m_pCtrl;

if(strKey.vt == VT_ERROR && strKey.scode == disP_E_ParaMNOTFOUND){
pitem->m_key = "";
}
else if(strKey.vt != VT_BSTR){
ColeVariant v;
v.ChangeType(VT_BSTR,(LPVARIANT)&strKey);
pitem->m_key = v.bstrVal;
}
else{
pitem->m_key = strKey.bstrVal;
}

if(strText.vt == VT_ERROR && strText.scode == disP_E_ParaMNOTFOUND){
pitem->m_text = "";
}
else if(strText.vt != VT_BSTR){
ColeVariant v;
v.ChangeType(VT_BSTR,(LPVARIANT)&strText);
pitem->m_text = v.bstrVal;
}
else{
pitem->m_text = strText.bstrVal;
}
m_olitems.AddTail(pitem);

if(m_pCtrl) m_pCtrl->InvalIDateControl();

return pitem->GetIdispatch(TRUE);
}

voID CColl::Remove(const VARIANT FAR& Index)
{
// Todo: Add your dispatch handler code here
if(Index.vt == VT_I4){
position pos = m_olitems.Findindex(Index.lVal);
delete m_olitems.GetAt(pos);
m_olitems.RemoveAt(pos);
}
else if(Index.vt == VT_BSTR){
position pos = m_olitems.Getheadposition();
position poscur = pos;
while(pos){
CTcItem* pitem = (CTcItem*)m_olitems.GetNext(pos);
if(pitem->m_key = Index.bstrVal){
delete pitem;
m_olitems.RemoveAt(poscur);
}
poscur = pos;
}
}

if(m_pCtrl) m_pCtrl->InvalIDateControl();

}

/*
LPdisPATCH CColl::Add(LPdisPATCH tcItem)
{
// Todo: Add your dispatch handler code here
CTcItem* pitem = (CTcItem*)CCmdTarget::FromIdispatch(tcItem);
m_olitems.AddTail(pitem);

if(m_pCtrl) m_pCtrl->InvalIDateControl();

return tcItem;
return NulL;
}
*/

注:
a.请看这一句 if(strKey.vt == VT_ERROR && strKey.scode == disP_E_ParaMNOTFOUND),这就是判断是否有设置缺省参数的,如果为TRUE,就表示未设置参数,你可以做相应的处理了,这里只是简单 的设置了缺省的参数。
b.这里在参数不是字符串时,都转换成字符串,也就是说,你可以直接写成.Add 1,1。
c..这里和元素类中一样,也定义了CTcollCtrl* m_pCtrl,用途也一样,也是用来调用InvalIDateControl刷新控件的。
d.这里有m_olitems,它的定义是CObList m_olitems;用来保存所有元素指针,应该不陌生吧,在集合类的析构函数中,需要删除所有的元素
CColl::~CColl()
{
position pos = m_olitems.Getheadposition();
while(pos){
delete m_olitems.GetNext(pos);
}
}
e.在
LPUNKNowN CColl::_NewEnum()
{
// Todo: Add your property handler here
m_xEnumVARIANT.m_posCurrent = m_olitems.Getheadposition();
LPUNKNowN pUnk = GetInterface(&IID_IEnumVARIANT);
if(pUnk) pUnk->AddRef();
return pUnk;
return NulL;
}
中,用到了m_xEnumVARIANT和IID_IEnumVARIANT,这就是接下来要做的,添加IEnumVARIANT接口

3.要能在VB中的for each语法中使用,一个很重要的部分就是要实现一个IEnumVARIANT接口,在for each时,VB会自动调用集合的_NewEnum来请求这个IEnumVARIANT接口(参见上面的_NewEnum实现代码)。 IEnumVARIANT接口可以在任何地方实现,只要能从_NewEnum中获得这个接口指针就可以了。我们这里还是用MFC的聚合类的方法来实现, m_xEnumVARIANT就是集合类中的聚合类对象,具体的方法不多述了,本系列的上面几步中已讲了很多了。

Coll.h
...
DECLARE_disPATCH_MAP()
DECLARE_INTERFACE_MAP()
public:
CTcollCtrl* m_pCtrl;
voID Draw(CDC* pdc);
CObList m_olitems;
BEGIN_INTERFACE_PART(EnumVARIANT,IEnumVARIANT)
STDMETHOD(Next)(THIS_ ulONG celt,VARIANT FAR* rgvar,
ulONG FAR* pceltFetched);
STDMETHOD(Skip)(THIS_ ulONG celt) ;
STDMETHOD(reset)(THIS) ;
STDMETHOD(Clone)(THIS_ IEnumVARIANT FAR* FAR* ppenum) ;
XEnumVARIANT() ; // constructor to set m_posCurrent
position m_posCurrent ; // Next() requires we keep track of our current item
END_INTERFACE_PART(EnumVARIANT)
...

Coll.cpp
...
BEGIN_INTERFACE_MAP(CColl,CCmdTarget)
INTERFACE_PART(CColl,IID_IColl,dispatch)
INTERFACE_PART(CColl,IID_IEnumVARIANT,EnumVARIANT) END_INTERFACE_MAP() CColl::XEnumVARIANT::XEnumVARIANT() { m_posCurrent = NulL ; } STDMETHODIMP_(ulONG) CColl::XEnumVARIANT::AddRef() { METHOD_PRolOGUE(CColl,EnumVARIANT) return pThis->ExternalAddRef() ; } STDMETHODIMP_(ulONG) CColl::XEnumVARIANT::Release() { METHOD_PRolOGUE(CColl,EnumVARIANT) return pThis->ExternalRelease() ; } STDMETHODIMP CColl::XEnumVARIANT::queryInterface( REFIID iID,voID FAR* FAR* ppvObj ) { METHOD_PRolOGUE(CColl,EnumVARIANT) return (HRESulT)pThis->ExternalqueryInterface( (voID FAR*)&iID,ppvObj) ; } // IEnumVARIANT::Next // STDMETHODIMP CColl::XEnumVARIANT::Next( ulONG celt,ulONG FAR* pceltFetched) { // This sets up the "pThis" pointer so that it points to our // containing Cdocuments instance // METHOD_PRolOGUE(CColl,EnumVARIANT) HRESulT hr; ulONG l ; CTcItem* pItem = NulL ; if(pceltFetched != NulL) *pceltFetched = 0; else if(celt > 1){ return ResultFromScode(E_INVALIDARG); } for(l=0; l<celt; L++){ Variantinit(&rgvar[l]); } hr = NOERROR; for(l=0; m_posCurrent != NulL && celt != 0; L++){ pItem = (CTcItem*)pThis->m_olitems.GetNext(m_posCurrent); celt--; if(pItem){ rgvar[l].vt = VT_disPATCH; rgvar[l].pdispVal = pItem->GetIdispatch(TRUE); if(pceltFetched != NulL) (*pceltFetched)++; } else{ return ResultFromScode( E_UNEXPECTED ); } } if (celt != 0) hr = ResultFromScode( S_FALSE ) ; return hr; } // IEnumVARIANT::Skip // STDMETHODIMP CColl::XEnumVARIANT::Skip(ulONG celt) { METHOD_PRolOGUE(CColl,EnumVARIANT) while(m_posCurrent != NulL && celt--) pThis->m_olitems.GetNext(m_posCurrent); return (celt == 0 ? NOERROR : ResultFromScode( S_FALSE )) ; } STDMETHODIMP CColl::XEnumVARIANT::reset() { METHOD_PRolOGUE(CColl,EnumVARIANT) m_posCurrent = pThis->m_olitems.Getheadposition(); return NOERROR ; } STDMETHODIMP CColl::XEnumVARIANT::Clone(IEnumVARIANT FAR* FAR* ppenum) { METHOD_PRolOGUE(CColl,EnumVARIANT) CColl* p = new CColl ; if (p) { p->m_xEnumVARIANT.m_posCurrent = m_posCurrent ; return NOERROR ; } else return ResultFromScode( E_OUTOFMEMORY ) ; } 注: a.在前面的_NewEnum属性实现中 LPUNKNowN CColl::_NewEnum() { // Todo: Add your property handler here m_xEnumVARIANT.m_posCurrent = m_olitems.Getheadposition(); LPUNKNowN pUnk = GetInterface(&IID_IEnumVARIANT); if(pUnk) pUnk->AddRef(); return pUnk; return NulL; } m_xEnumVARIANT.m_posCurrent = m_olitems.Getheadposition(); 这一句就是用来设置枚举的起始位置的。 4.集合类基本完毕,现在是将集合类和控件类联起来的时候了,这个很简单,为控件类加一个LPdisPATCH Coll只读属性,另外由于集合类需要控件类的指针,所以在构造函数中将控件类的指针传递给了集合类的m_pCtrl成员,同样在集合的元素类中,也会需 要控件类的指针以及时刷新控件,这个指针的传递在集合类的Add方法中 LPdisPATCH CColl::Add(const VARIANT FAR& strKey,const VARIANT FAR& strText) { // Todo: Add your dispatch handler code here CTcItem* pitem = new CTcItem; pitem->m_pCtrl = m_pCtrl; ...... } TcollCtl.cpp ...... CTcollCtrl::CTcollCtrl() { InitializeIIDs(&IID_DTcoll,&IID_DTcollEvents); // Todo: Initialize your control's instance data here. m_pColl = new CColl; m_pColl->m_pCtrl = this; } ///////////////////////////////////////////////////////////////////////////// // CTcollCtrl::~CTcollCtrl - Destructor CTcollCtrl::~CTcollCtrl() { // Todo: Cleanup your control's instance data here. delete m_pColl; } ... LPdisPATCH CTcollCtrl::GetColl() { // Todo: Add your property handler here return m_pColl->GetIdispatch(TRUE); return NulL; } tcoll.odl ...... [ uuID(7852D333-7E2F-49DA-BA8F-EAC8748A23B2),helpstring("dispatch interface for Tcoll Control"),hIDden ] dispinterface _DTcoll { propertIEs: // NOTE - ClassWizard will maintain property information here. // Use extreme caution when editing this section. //{{AFX_ODL_PROP(CTcollCtrl) [ID(1)] IColl* Coll; //}}AFX_ODL_PROP methods: // NOTE - ClassWizard will maintain method information here. // Use extreme caution when editing this section. //{{AFX_ODL_METHOD(CTcollCtrl) //}}AFX_ODL_METHOD [ID(disPID_ABOUTBox)] voID AboutBox(); }; ...... 5.整个框架好了,现在还需要一个外在的表现了,OnDraw函数上场了。 voID CTcollCtrl::OnDraw( CDC* pdc,const CRect& rcBounds,const CRect& rcInvalID) { // Todo: Replace the following code with your own drawing code. pdc->FillRect(rcBounds,CBrush::FromHandle((HBrush)GetStockObject(WHITE_Brush))); m_pColl->Draw(pdc); } voID CColl::Draw(CDC *pdc) { int x = 5; int y = 5; position pos = m_olitems.Getheadposition(); while(pos){ CTcItem* pitem = (CTcItem*)m_olitems.GetNext(pos); pdc->textout(x,y,pitem->m_text); y += 20; } } 控件类先画底,然后调用集合类的Draw函数,在集合类的Draw函数中将所有集合元素中的Text属性列了出来。 6.编译,在VB下测试 Private Sub Form_Load() With Tcoll1 .Coll.Add "keyHello","Hello" .Coll.Add,"Good" .Coll.Add "keyThank" MsgBox .Coll("keyHello").Text Dim c As TcItem For Each c In .Coll MsgBox c.Text & c.Key Next End With End Sub 参考资料:刚刚发现,很多关于ActiveX和olE的资料 MSDN98/98VS/2052/techart.chm 本文的相关参考也在其中 MSDN98/98VS/2052/techart.chm::/HTML/msdn_collect.htm 总结

以上是内存溢出为你收集整理的ActiveX控件的MFC设计之旅-第11步 .全部内容,希望文章能够帮你解决ActiveX控件的MFC设计之旅-第11步 .所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/1291246.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-06-10
下一篇 2022-06-10

发表评论

登录后才能评论

评论列表(0条)

保存