Design of ActiveX Control Using MFC-Step 2

Source: Internet
Author: User
Tags getcolor
In this step, we will persist an interface, which may be difficult, because we have used a lot of things we mentioned earlier.

In step 1, we implemented a hierarchical control, but we didn't provide persistence for this control, let alone all layers of the control.

In the previous step, the persistence of a standard image interface (IPictureDisp) is realized.

In this step, we will combine the content in the first step and the previous step to realize the persistence of a custom interface.

The Topp example in the previous step is still used (it is basically independent from the content in the previous step. You can create a control from scratch)
1. as described in step 1, create a new Dispatch interface IToppBox (CToppBox derived from cshorttarget, using the Createable by type ID Option). This interface provides borders for our controls.
2. Add the long Width attribute and OLE_COLOR attribute to the new IToppBox interface (The implementation class is CToppBox ).
3. Add the LPDISPATCH Box attribute to the control class CToppCtrl and set the variable to LPDISPATCH m_pboxdisp;

CToppCtrl: CToppCtrl ()
{
InitializeIIDs (& IID_DTopp, & IID_DToppEvents );

// TODO: Initialize your control's instance data here.
M_pboxdisp = NULL;
}

LPDISPATCH CToppCtrl: GetBox ()
{
// TODO: Add your property handler here
M_pboxdisp-> AddRef ();
Return m_pboxdisp;
Return NULL;
}

Void CToppCtrl: SetBox (LPDISPATCH newValue)
{
// TODO: Add your property handler here
If (m_pboxdisp! = NULL) m_pboxdisp-> Release ();
M_pboxdisp = newValue;
M_pboxdisp-> AddRef ();
InvalidateControl ();
SetModifiedFlag ();
}

4. Modify. odl and change the type of the Box attribute to IToppBox *. For details, refer to step 1 of this series.

5. Draw a border in OnDraw

Void CToppCtrl: 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 )));
Pdc-> Ellipse (rcBounds );*/
// Draw a border
CToppBox * pbox = (CToppBox *) c?target: FromIDispatch (m_pboxdisp );
Int nwid = pbox-> m_width;
If (nwid> 0 ){
COLORREF crb = TranslateColor (pbox-> m_color );
CPen pen (PS_SOLID, nwid, crb );
CPen * ppenold = pdc-> SelectObject (& pen );
Pdc-> Rectangle (& rcBounds );
Pdc-> SelectObject (ppenold );
}

// Draw an image
CRect rcfill = rcBounds;
Rcfill. DeflateRect (nwid, nwid );

Pdc-> FillRect (& rcfill, & CBrush (TranslateColor (m_color )));
M_pic.Render (pdc, rcfill, rcBounds );

// Draw a String Array
Int nhei = 18;
Int y = 2;
For (int I = 0; I <m_saItems.GetSize (); I ++, y + = nhei ){
CRect rect (rcBounds. left + 4, y + 2, rcBounds. right-4, y + nhei );
Pdc-> FillRect (& rect, & CBrush (RGB (255,255,255 )));
Pdc-> TextOut (rcBounds. left + 4, y + 2, m_saItems [I]);
}
}

Note: here, we can directly call the csf-target: FromIDispatch method to obtain the m_pboxdisp implementation class pointer to obtain m_width and m_color data (if this method is used, you can also add a void Draw (CDC * pDC) function to the CToppBox class to implement plotting directly. You can also use the COleDispatchDriver class to obtain the attributes of the IToppBox (of course, in this way, the Draw method cannot be used) to Draw the corresponding border. In the design of the property page, we will use this method to obtain and set the properties of the control, for reference.
6. Now, we have added an LPDISPATCH type attribute for our controls. Our first goal is to implement a hierarchical control, which has been completed. Next is the persistence part.

7. Create a dialog box template to connect the CBoxPropPage class generated from the COlePropertyPage.
A. this property page is actually used to set properties for our IToppBox interface object, so we added a member variable LPDISPATCH m_pboxdisp; (although LPDISPATCH is used, but it is actually our IToppBox interface pointer)
B. we add two functions GetBoxDisp and SetBoxDisp to obtain and set m_pboxdisp.

Void CBoxPropPage: GetBoxDisp ()
{
USES_CONVERSION;
COleDispatchDriver PropDispDriver;
ULONG nObjects = 0;
LPDISPATCH * ppDisp = GetObjectArray (& nObjects );
DISPID dwDispID;
LPCOLESTR lpOleStr = T2COLE ("Box ");
PpDisp [0]-> GetIDsOfNames (IID_NULL, (LPOLESTR *) & lpOleStr, 1, 0, & dwDispID );

If (m_pboxdisp! = NULL ){
M_pboxdisp-> Release ();
M_pboxdisp = NULL;
}
PropDispDriver. AttachDispatch (ppDisp [0]);
PropDispDriver. GetProperty (dwDispID, VT_DISPATCH, (void *) & m_pboxdisp );
PropDispDriver. DetachDispatch ();
}

Void CBoxPropPage: SetBoxDisp ()
{
USES_CONVERSION;
COleDispatchDriver PropDispDriver;
ULONG nObjects = 0;
LPDISPATCH * ppDisp = GetObjectArray (& nObjects );

DISPID dwDispID;
LPCOLESTR lpOleStr = T2COLE ("Box ");
PpDisp [0]-> GetIDsOfNames (IID_NULL, (LPOLESTR *) & lpOleStr, 1, 0, & dwDispID );

PropDispDriver. AttachDispatch (ppDisp [0]);
PropDispDriver. SetProperty (dwDispID, VT_DISPATCH, m_pboxdisp );
PropDispDriver. DetachDispatch ();
}

C. associate with the control class

Void CBoxPropPage: DoDataExchange (CDataExchange * pDX)
{
// NOTE: ClassWizard will add DDP, DDX, and DDV cballs here
// Do not edit what you see in these blocks of generated code!
// {AFX_DATA_MAP (CBoxPropPage)
DDX_Text (pDX, IDC_EDIT_WIDTH, m_nWidth );
//} AFX_DATA_MAP
If (pDX-> m_bSaveAndValidate ){
/* If (m_bNewDisp ){
SetBoxDisp ();
}
Refresh ();*/
SetBoxDisp ();
}
Else {
GetBoxDisp ();
}
DDP_PostProcessing (pDX );
}

D. to start setting the actual attributes, add an edit box (IDC_EDIT_WIDTH) in the dialog box template (IDD_PROPPAGE_BOX) to set the Width attribute, a button ("color") and a static text box (IDC_STATIC_COLOR) to set the Color attribute.
E. Add the GetColor, SetColor, GetWidth, and SetWidth functions.

COLORREF CBoxPropPage: GetColor ()
{
USES_CONVERSION;
COLORREF cr = RGB (255, 0, 0 );
If (m_pboxdisp ){
COleDispatchDriver PropDispDriver;
DISPID dwDispID;
LPCOLESTR lpOleStr = T2COLE ("Color ");
If (SUCCEEDED (m_pboxdisp-> GetIDsOfNames (IID_NULL, (LPOLESTR *) & lpOleStr, 1, 0, & dwDispID )))
{
PropDispDriver. AttachDispatch (m_pboxdisp, FALSE );
OLE_COLOR ocr;
PropDispDriver. GetProperty (dwDispID, VT_COLOR, & ocr );
PropDispDriver. DetachDispatch ();
: OleTranslateColor (ocr, NULL, & cr );
}
}
Return cr;
}

Void CBoxPropPage: SetColor (COLORREF crborder)
{
USES_CONVERSION;
If (m_pboxdisp ){
COleDispatchDriver PropDispDriver;
DISPID dwDispID;
LPCOLESTR lpOleStr = T2COLE ("Color ");
If (SUCCEEDED (m_pboxdisp-> GetIDsOfNames (IID_NULL, (LPOLESTR *) & lpOleStr, 1, 0, & dwDispID )))
{
PropDispDriver. AttachDispatch (m_pboxdisp, FALSE );
PropDispDriver. SetProperty (dwDispID, VT_COLOR, crborder );
PropDispDriver. DetachDispatch ();
}
}
}

Long CBoxPropPage: GetWidth ()
{
USES_CONVERSION;
Long l = 1;
If (m_pboxdisp ){
COleDispatchDriver PropDispDriver;
DISPID dwDispID;
LPCOLESTR lpOleStr = T2COLE ("Width ");
If (SUCCEEDED (m_pboxdisp-> GetIDsOfNames (IID_NULL, (LPOLESTR *) & lpOleStr, 1, 0, & dwDispID )))
{
PropDispDriver. AttachDispatch (m_pboxdisp, FALSE );
PropDispDriver. GetProperty (dwDispID, VT_I4, & l );
PropDispDriver. DetachDispatch ();
}
}
Return l;
}

Void CBoxPropPage: SetWidth (long l)
{
USES_CONVERSION;
If (m_pboxdisp ){
COleDispatchDriver PropDispDriver;
DISPID dwDispID;
LPCOLESTR lpOleStr = T2COLE ("Width ");
If (SUCCEEDED (m_pboxdisp-> GetIDsOfNames (IID_NULL, (LPOLESTR *) & lpOleStr, 1, 0, & dwDispID )))
{
PropDispDriver. AttachDispatch (m_pboxdisp, FALSE );
PropDispDriver. SetProperty (dwDispID, VT_I4, l );
PropDispDriver. DetachDispatch ();
}
}
}

In the previous steps, we said that GetPropText and SetPropText can be used to simplify the reading and setting of attributes. However, the premise here is that this attribute is the property of the control, but now the attribute to be set on our property page is IToppBox, so we have to use the COleDispatchDriver to implement these functions.

F. Response to the IDC_EDIT_WIDTH string to change the message, response to the WM_CTLCOLOR message processing function, response button ("color") Message Processing.

Void CBoxPropPage: OnChangeEditWidth ()
{
// TODO: If this is a RICHEDIT control, the control will not
// Send this notification unless you override the COlePropertyPage: OnInitDialog ()
// Function and call CRichEditCtrl (). SetEventMask ()
// With the ENM_CHANGE flag ORed into the mask.

// TODO: Add your control notification handler code here
UpdateData ();
SetWidth (m_nWidth );
}

HBRUSH CBoxPropPage: OnCtlColor (CDC * pDC, CWnd * pWnd, UINT nCtlColor)
{
HBRUSH hbr = COlePropertyPage: OnCtlColor (pDC, pWnd, nCtlColor );

// TODO: Change any attributes of the DC here
If (nCtlColor = CTLCOLOR_STATIC ){
COLORREF cr = GetColor ();
Return: CreateSolidBrush (cr );
}
Else {
// TODO: Return a different brush if the default is not desired
Return hbr;
}
}

Void CBoxPropPage: OnButtonColor ()
{
// TODO: Add your control notification handler code here
CColorDialog dlg;
If (dlg. DoModal () = IDOK ){
SetColor (dlg. GetColor ());
GetDlgItem (IDC_STATIC_COLOR)-> Invalidate ();
}
}

G. in addition, we add a button ("new") and a member variable BOOL m_bNewDisp to indicate whether we create an IToppBox object or use the IToppBox object originally obtained from the control, that is, use the following pseudocode:
New
Box = new boxdisp;
Box. width = 10;
Box. color = 32048;
Top. box = box;
Non-New
Topp. box. width = 10;
Topp. box. color = 32048;

The following is the implementation code

Void CBoxPropPage: OnButtonNew ()
{
// TODO: Add your control notification handler code here
M_bNewDisp = TRUE;
CToppBox * pbox = new CToppBox;
M_pboxdisp-> Release ();
// The reference parameter + 1 has been referenced in the CToppBox constructor, so here the parameter uses FALSE
M_pboxdisp = pbox-> GetIDispatch (FALSE );
}

In the previous OnDataExchange
Void CBoxPropPage: DoDataExchange (CDataExchange * pDX)
{
// NOTE: ClassWizard will add DDP, DDX, and DDV cballs here
// Do not edit what you see in these blocks of generated code!
// {AFX_DATA_MAP (CBoxPropPage)
DDX_Text (pDX, IDC_EDIT_WIDTH, m_nWidth );
//} AFX_DATA_MAP
If (pDX-> m_bSaveAndValidate ){
// You do not call SetBoxDisp () when you do not create a new control. However, to Refresh the control, you must provide the Refresh method.
/* If (m_bNewDisp ){
SetBoxDisp ();
}
Refresh ();*/
SetBoxDisp ();
}
Else {
GetBoxDisp ();
}
DDP_PostProcessing (pDX );
}

We also use SetBoxDisp () directly, because we need to use the InvalidateControl of the control class to refresh the control.

Void CToppCtrl: SetBox (LPDISPATCH newValue)
{
// TODO: Add your property handler here
If (m_pboxdisp! = NULL) m_pboxdisp-> Release ();
M_pboxdisp = newValue;
M_pboxdisp-> AddRef ();
InvalidateControl ();
SetModifiedFlag ();
}

H. when m_pboxdisp is used up and the attribute page ends, m_pboxdisp needs to be released. The headache is that the Destructor on the attribute page does not know where to run, so I have to release it in OnDestroy, if anyone knows how to use the destructor, please help me. Thank you.

Void CBoxPropPage: OnObjectsChanged ()
{
ULONG ul = 0;
GetObjectArray (& ul );
If (ul = 0 ){
If (m_pboxdisp ){
M_pboxdisp-> Release ();
M_pboxdisp = NULL;
}
}
}

Void CBoxPropPage: OnDestroy ()
{
COlePropertyPage: OnDestroy ();

// TODO: Add your message handler code here
If (m_pboxdisp ){
M_pboxdisp-> Release ();
M_pboxdisp = NULL;
}
}

It may be a bit cool. However, the property page is finally finished, and now is the time for true persistence.

8. Add the function BOOL PX_Box (CPropExchange * pPX) and call it by DoPropExchange.

Void CToppCtrl: DoPropExchange (CPropExchange * pPX)
{
ExchangeVersion (pPX, MAKELONG (_ wVerMinor, _ wVerMajor ));
COleControl: DoPropExchange (pPX );

// TODO: Call PX _ functions for each persistent custom property.
PX_Color (pPX, "Color", m_color, TranslateColor (RGB (0, 0, 0 )));
PX_Picture (pPX, _ T ("Picture"), m_pic );
PX_Box (pPX );
PX_Items (pPX );
}
Only PX_Box is required in this step, and several other PX_xxx are the content of the previous steps in this series. Ignore them.

9. Implement PX_Box

BOOL CToppCtrl: PX_Box (CPropExchange * pPX)
{
CToppBox * pbox = new CToppBox;
LPDISPATCH pboxdisp = pbox-> GetIDispatch (FALSE );
LPUNKNOWN & pUnkd = (LPUNKNOWN &) pboxdisp;
LPUNKNOWN & pUnk = (LPUNKNOWN &) m_pboxdisp;
PX_IUnknown (pPX, _ T ("Box"), pUnk, IID_IDispatch, pUnkd );
PUnkd-> Release ();
Return TRUE;
}

10. It seems to be good, but unfortunately, if you compile the test, you will find that it cannot be persisted at all. Why is it very simple? Our CToppBox does not provide actual persistence operations. Therefore, two methods are proposed. One is to read and write data to CPropExchange without using PX_IUnknown, which is the same as PX_Blob described in the previous steps of this series, however, the IToppBox interface is not quite universal. The other method is to implement the persistent interface in CToppBox. This article uses 2nd methods.
The following is part of the code when MFC implements interface persistence.
LPPERSISTSTREAM pps = NULL;
If (SUCCEEDED (* ppUnk)-> QueryInterface (
IID_IPersistStream, (void **) & pps) |
SUCCEEDED (* ppUnk)-> QueryInterface (
IID_IPersistStreamInit, (void **) & pps )))
{
ASSERT_POINTER (pps, IPersistStream );
BResult = SUCCEEDED (pps-> Load (pstm ));
Pps-> Release ();
}
As you can see, it will request the IPersistStream or IPersistStreamInit interface, so we have to let our CToppBox implement these interfaces (these principles are hard to say, and we will not discuss them here ), here we only implement IPersistStreamInit. During OLE embedding, you also need to implement IPersistStorage and Other interfaces. If you are interested, you can implement them one by one.

It is not complicated to implement these interfaces in MFC. We also mentioned the article about adding double interfaces in the previous steps of this series.
Add some code in ToppBox. h.
Public:
Virtual HRESULT GetClassID (LPCLSID pclsid );
HRESULT SaveState (IStream * pstm );
HRESULT LoadState (IStream * pstm );
BOOL m_bModified;
// IPersistStreamInit
BEGIN_INTERFACE_PART (PersistStreamInit, IPersistStreamInit)
INIT_INTERFACE_PART (CToppBox, PersistStreamInit)
STDMETHOD (GetClassID) (LPCLSID );
STDMETHOD (IsDirty )();
STDMETHOD (Load) (LPSTREAM );
STDMETHOD (Save) (LPSTREAM, BOOL );
STDMETHOD (GetSizeMax) (ULARGE_INTEGER *);
STDMETHOD (InitNew )();
END_INTERFACE_PART_STATIC (PersistStreamInit)
Implement interface functions in ToppBox. cpp

STDMETHODIMP _ (ULONG) CToppBox: XPersistStreamInit: AddRef ()
{
METHOD_PROLOGUE_EX _ (CToppBox, PersistStreamInit)
Return (ULONG) pThis-> ExternalAddRef ();
}

STDMETHODIMP _ (ULONG) CToppBox: XPersistStreamInit: Release ()
{
METHOD_PROLOGUE_EX _ (CToppBox, PersistStreamInit)
Return (ULONG) pThis-> ExternalRelease ();
}

STDMETHODIMP CToppBox: XPersistStreamInit: QueryInterface (
REFIID iid, LPVOID * ppvObj)
{
METHOD_PROLOGUE_EX _ (CToppBox, PersistStreamInit)
Return (HRESULT) pThis-> ExternalQueryInterface (& iid, ppvObj );
}

STDMETHODIMP CToppBox: XPersistStreamInit: GetClassID (maid)
{
METHOD_PROLOGUE_EX _ (CToppBox, PersistStreamInit)
Return pThis-> GetClassID (lpClassID );
}

STDMETHODIMP CToppBox: XPersistStreamInit: IsDirty ()
{
METHOD_PROLOGUE_EX _ (CToppBox, PersistStreamInit)
Return pThis-> m_bModified? S_ OK: S_FALSE;
}

STDMETHODIMP CToppBox: XPersistStreamInit: Load (LPSTREAM pStm)
{
METHOD_PROLOGUE_EX (CToppBox, PersistStreamInit)
Return pThis-> LoadState (pStm );
}

STDMETHODIMP CToppBox: XPersistStreamInit: Save (LPSTREAM pStm,
BOOL fClearDirty)
{
METHOD_PROLOGUE_EX (CToppBox, PersistStreamInit)

// Delegate to SaveState.
HRESULT hr = pThis-> SaveState (pStm );

// Bookkeeping: Clear the dirty flag, if requested.
If (fClearDirty)
PThis-> m_bModified = FALSE;

Return hr;
}

STDMETHODIMP CToppBox: XPersistStreamInit: GetSizeMax (ULARGE_INTEGER *)
{
Return E_NOTIMPL;
}

STDMETHODIMP CToppBox: XPersistStreamInit: InitNew ()
{
METHOD_PROLOGUE_EX (CToppBox, PersistStreamInit)

// Delegate to OnResetState.
// PThis-> OnResetState ();

// Unless IOleObject: SetClientSite is called after this, we can
// Count on ambient properties being available while loading.
// PThis-> m_bCountOnAmbients = TRUE;

// Properties have been initialized
// PThis-> m_bInitialized = TRUE;

// Uncache cached ambient properties
// _ AfxAmbientCache-> Cache (NULL );

Return S_ OK;
}

It can be found that in the above implementation, the really important saving and Loading Code is actually in the LoadState and SaveState functions of CToppBox. Therefore, add these two functions.

HRESULT CToppBox: LoadState (IStream * pstm)
{
DWORD dw = 0;
Pstm-> Read (& m_width, sizeof (long), & dw );
Pstm-> Read (& m_color, sizeof (OLE_COLOR), & dw );
Return S_ OK;
}

HRESULT CToppBox: SaveState (IStream * pstm)
{
DWORD dw = 0;
Pstm-> Write (& m_width, sizeof (long), & dw );
Pstm-> Write (& m_color, sizeof (OLE_COLOR), & dw );
Return S_ OK;
}

In addition, the GetClassID function is required.

HRESULT CToppBox: GetClassID (LPCLSID pclsid)
{
// The guid is actually defined in the IMPLEMENT_OLECREATE macro.
* Pclsid = guid;
Return NOERROR;
}
And member variables
BOOL m_bModified;

Next, add the IPersistStreamInit interface to the interface ing table.

BEGIN_INTERFACE_MAP (CToppBox, CCmdTarget)
INTERFACE_PART (CToppBox, IID_IToppBox, Dispatch)
INTERFACE_PART (CToppBox, IID_IPersistStreamInit, PersistStreamInit)
END_INTERFACE_MAP ()

11. It seems that there are too many things and there may be something missing. If you are interested, please point it out.

Attach the controls and properties page (Please note that the pictures and other content are the effect of the previous steps in this series)

Related Article

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.