Design of ActiveX Control Using MFC-Step 2

Source: Internet
Author: User
OLE automated collection class

The following syntax is available in VB:

Set docs = Application.Documents
For Each doc in docs
  MsgBox doc.Title
Next

Here, docs is a Collection class, and VB also provides a Collection object to build your own Collection class.
Of course, the collection class also has many features. A collection class will have the following methods or attributes:
Count read-only attribute
Item method. It can contain one or more parameters and returns objects in the set. It is generally set to the default attribute (method) of the Set class)
_ NewEnum: Read-only attribute. This attribute is invisible to users and is mainly used in the preceding for each syntax.
In general, there may be methods such as Add and Remove.

Next, we will implement this collection class Tcoll step by step.

The previous steps are not mentioned.

1. create a new CTcItem class derived from csf-target, select the Createable by type ID option, and add the BSTR Key and BSTR Text attributes as the elements in our set. The attribute Key is the keyword, it is used to select collection elements using keywords. attribute Text is the element Text, which is used for display in controls. This is a very simple class.
In addition, we add a control pointer CTcollCtrl * m_pCtrl for CTcItem to refresh the control when the Text attribute is changed.
Class CTcollCtrl;
Class CTcItem: public c0000target
{
...
Public:
CTcollCtrl * m_pCtrl;
CString m_key;
CString m_text;
...
}
When m_key and m_text are generated using the wizard, they are protected. However, here they are moved to public, because they will be used later.

CTcItem: CTcItem ()
{
EnableAutomation ();

// To keep the application running as long as an OLE automation
// The object is active, the constructor callafxolelockapp.
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. create a new CColl class derived from c1_target and select the Automation option. As our collection class, add the long Count read-only attribute, LPDISPATCH Item (VARIANT Index) method, and LPUNKNOWN NewEnum attribute, LPDISPATCH Add (VARIATN strKey, VARIANT strText) method and void Remove (VARIANT Index) Method

For the Item method, you need to set it as the Default Property. However, it seems that the wizard does not provide the Default Property Option for the method, so we have to add it ourselves, the following DISP_DEFVALUE macro is used to set the default attribute. In addition, you must change the DispID of Item to 0.
NewEnum is very special, because its name is not important. You can change it to another dog or cat, but its DispID must be DISPID_NEWENUM =-4
So the above changes are completed in two places, one is in. cpp of the Collection class, and the other is in. odl, as follows:

Coll. cpp:
BEGIN_DISPATCH_MAP (CColl, CColl target)
// {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, SetNotSupported, VT_UNKNOWN)
DISP_FUNCTION (CColl, "Remove", Remove, VT_EMPTY, VTS_VARIANT)
DISP_FUNCTION (CColl, "Add", Add, VT_DISPATCH, 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

};

Note:
A. here, both _ NewEnum and Item are mentioned outside the code generated by the Wizard to avoid modifying DispID in The Wizard when adding new properties or methods. Obviously, the wizard does not know DispID such as DISPID_NEWENUM.
B. Here, the return values of Item and Add are changed from IDispatch * To ITcItem *, so that the interface attributes or methods can be displayed during VB code design.
C. here, the optional option is added to both parameters of the Add method to use the default parameter, just like the ListItems object Add method ListView1.ListItems of the ListView control. add, "Hello" is used in the same way. If you want to use the default parameter, you must use VARIANT. Therefore, VARIANT instead of BSTR is used here.
D. Here, the indexes in Item and Remove use VARIANT, because we may use keywords to select elements in the set.
E. the Add method for adding elements to a set is determined by yourself. Here, an element is generated by a function. You can also Add an IDispatch (ITcItem) directly) element to the Set (createable by type ID Option needs to be set when csf-target is used for derivation). The following code commented out contains the implementation of this method, which is for reference only.

According to the above description, it is not difficult to write the following implementation code

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 *) csf-target: FromIDispatch (tcItem );
M_olItems.AddTail (pitem );

If (m_pCtrl) m_pCtrl-> InvalidateControl ();

Return tcItem;
Return NULL;
}
*/

Note:
A. please refer to this sentence if (strKey. vt = VT_ERROR & strKey. scode = DISP_E_PARAMNOTFOUND). This is to determine whether the default parameter is set. If it is TRUE, it indicates that no parameter is set. You can perform corresponding processing, here, we simply set the default parameters.
B. When the parameter is not a string, it is converted to a string. That is to say, you can directly write it as. Add 1, 1.
C. Like the element class, CTcollCtrl * m_pCtrl is also defined and used to call InvalidateControl to refresh the control.
D. Here m_olItems is available, which is defined as CObList m_olItems. It is no stranger to store all element pointers. In the collection class destructor, You need to delete all elements.
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 and IID_IEnumVARIANT are used. This is what we will do next. Add the IEnumVARIANT interface.

3. to use the for each syntax in VB, an important part is to implement an IEnumVARIANT interface, VB will automatically call the set's _ NewEnum to request this IEnumVARIANT interface (see the above _ NewEnum implementation code ). The IEnumVARIANT interface can be implemented anywhere, as long as the interface pointer can be obtained from _ NewEnum. Here we will use the methods of the MFC aggregate class to implement it. m_xEnumVARIANT is the aggregate class object in the Collection class. The specific methods are not described much. The previous steps in this series have already mentioned a lot.

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 * 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, CColl target)
INTERFACE_PART (CColl, IID_IColl, Dispatch)
INTERFACE_PART (CColl, IID_IEnumVARIANT, EnumVARIANT)
END_INTERFACE_MAP ()

CColl: 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 * ppvObj)
{
METHOD_PROLOGUE (CColl, EnumVARIANT)
Return (HRESULT) pThis-> ExternalQueryInterface (void FAR *) & iid, ppvObj );
}

// IEnumVARIANT: Next
//
STDMETHODIMP CColl: XEnumVARIANT: Next (ULONG celt, variant far * rgvar, 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 * 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 );
}

Note:
A. In the previous _ NewEnum attribute implementation
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 ();
This is used to set the start position of enumeration.

4. the Collection class is basically complete. Now it is time to link the collection class with the control class. It is very easy to add an LPDISPATCH Coll read-only attribute to the control class. In addition, because the collection class requires a pointer to the control class, therefore, in the constructor, the pointer of the control class is passed to the m_pCtrl Member of the Set class. Similarly, in the element class of the set, the pointer of the control class and the control should be refreshed at the time, this pointer is passed in the Add method of the Collection class.
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. The entire framework is ready, and now an external performance is needed. The OnDraw function is ready.

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;
}
}
The control class draws the bottom, then calls the Draw function of the Collection class, and lists the Text attribute of all the collection elements in the Draw function of the Collection class.

6. Compile and test it in 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
End Sub

References: I have just found many materials about ActiveX and OLE.
MSDN98/98VS/2052/techart. chm
The references in this article are also included.
MSDN98/98VS/2052/techart. chm:/html/msdn_collect.htm

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.