YOU CAN CODE!

 

With The Case Of UCanCode.net  Release The Power OF  Visual C++ !   HomeProducts | PurchaseSupport | Downloads  
Download Evaluation
Pricing & Purchase?
E-XD++Visual C++/ MFC Products
Overview
Features Tour 
Electronic Form Solution
Visualization & HMI Solution
Power system HMI Solution
CAD Drawing and Printing Solution

Bar code labeling Solution
Workflow Solution

Coal industry HMI Solution
Instrumentation Gauge Solution

Report Printing Solution
Graphical modeling Solution
GIS mapping solution

Visio graphics solution
Industrial control SCADA &HMI Solution
BPM business process Solution

Industrial monitoring Solution
Flowchart and diagramming Solution
Organization Diagram Solution

Graphic editor Source Code
UML drawing editor Source Code
Map Diagramming Solution

Architectural Graphic Drawing Solution
Request Evaluation
Purchase
ActiveX COM Products
Overview
Download
Purchase
Technical Support
  General Q & A
Discussion Board
Contact Us

Links

Get Ready to Unleash the Power of UCanCode .NET


UCanCode Software focuses on general application software development. We provide complete solution for developers. No matter you want to develop a simple database workflow application, or an large flow/diagram based system, our product will provide a complete solution for you. Our product had been used by hundreds of top companies around the world!

"100% source code provided! Free you from not daring to use components because of unable to master the key technology of components!"


VC++ MFC Tutorial: Property Pages for ActiveX Controls, COlePropertyPage, GetIDispatch

 
 

I am not going to reinvent the wheel and everything I am going to talk about is documented here and there. The only reason I am putting all this in one place is because I spent a week to get all parts together.

ActiveX introduced a new way to display the propery pages. Each page is a separate control. You may find the GUID for these controls in the registry, but you cannot find them using "Object viewer" or importing the type library of the main ActiveX control.

How to create a property page for your control? This is not a big deal and this part is well documented and supplied with numerous examples. You simply follow these steps:

  1. Create a new dialog resource (size 250x62 or 250x110 dialog units) and using ClassWizard add to your project a new class derived from COlePropertyPage, e.g. class COptionsPropPage : public COlePropertyPage
  2. Create two new string resources (add them to your string table resource): one - for the caption of your new property page and another one - for the property page name (do you remember - property page is an object?)
  3. At the top of *.cpp file, created for your property page, find the following functions and replace 0-s (otherwise regsvr32 will crash:
    /////////////////////////////////////////////////////////////////////////////
    // COptionsPropPage::COptionsPropPageFactory::UpdateRegistry-
    // Adds or removes system registry entries for COptionsPropPage
    
    BOOL COptionsPropPage::COptionsPropPageFactory::UpdateRegistry(BOOL bRegister)
    {
       // TODO: Define string resource for page type; replace '0' below with ID.
    
            if (bRegister)
                return AfxOleRegisterPropertyPageClass(AfxGetInstanceHandle(),
                    m_clsid, IDS_PPG_OPTIONS);
            else
                return AfxOleUnregisterClass(m_clsid, NULL);
    }
    
    /////////////////////////////////////////////////////////////////////////////
    // COptionsPropPage::COptionsPropPage - Constructor
    // TODO: Define string resource for page caption; replace '0' below with ID.
    
    COptionsPropPage::COptionsPropPage() :
            COlePropertyPage(IDD, IDS_PPG_OPTIONS_CAPTION)
    {
        //{{AFX_DATA_INIT(CDJNasdaqLIIOptionsPropPage)
        //}}AFX_DATA_INIT
    }
  4. Add *.h file of your new property page to *.cpp file of your control and modify the following macros as follows:
    // TODO: Add more property pages as needed.  Remember to increase the count!
    BEGIN_PROPPAGEIDS(CDoSomethingCtrl, 4)
    	PROPPAGEID(COptionsPropPage::guid)
    	PROPPAGEID(CLSID_CColorPropPage)
    	PROPPAGEID(CLSID_CFontPropPage)
    	PROPPAGEID(CLSID_CPicturePropPage)
    END_PROPPAGEIDS(CDJNasdaqLIICtrl)

    Pay attention that last three lines will automatically insert stock property pages for color, font and pictures respectively (you don't have to create them separately, Microsoft did it for you)

Case 1: Your control wants to display its own property sheet in runtime mode.

This case is simple one, but is not documented. You have to do the following:

  1. Add GetPages() method to the implementation file of your control:
    // This method returns array of property pages used then by
    // OLE container to bring property pages to the user
    STDMETHODIMP CDoSomethingCtrl::"#630000">GetPages(CAUUID *pPages)
    {
        GUID *pGUID;
        const unsigned CPROPPAGES = 4;
    
        pPages->cElems = 0;
        pPages->pElems = NULL;
    
        pGUID = (GUID*) CoTucancode.netMemAlloc( CPROPPAGES * sizeof(GUID) );
    
        if( NULL == pGUID )
        {
            return ResultFromScode(E_OUTOFMEMORY);
        }
    
        // Fill the array of property pages now
        pGUID[0] = COptionsPropPage::guid;
        pGUID[2] = CLSID_CFontPropPage;
        pGUID[3] = CLSID_CColorPropPage;
        pGUID[4] = CLSID_CPicturePropPage;
    
        //Fill the structure and return
        pPages->cElems = CPROPPAGES;
        pPages->pElems = pGUID;
        return NOERROR;
    }

    Pay attention that you may have different set of pages from your original one. You are not comitted to display the same property pages in design- and runtime modes.

  2. Add OnShowProperties method to the implementation file of your control:
    // This method is usually implemented by container to display
    // the properties of a control at runtime. We need it for the control
    // itself to display property pages at runtime.
    void CDoSomethingCtrl::OnShowProperties()
    {
    	CAUUID	caGUID;
    	HRESULT	hr;
    	LPDISPATCH pIDispatch = GetIDispatch(TRUE);
    	LCID lcid = AmbientLocaleID();
    
    	GetPages(&caGUID);
    
    	hr = OleCreatePropertyFrame(
    			m_hWnd,
    			10,
    			10,
    			OLESTR("Do something control"),
    			1,
    			(IUnknown**) &pIDispatch,
    			caGUID.cElems,
    			caGUID.pElems,
    			lcid,
    			0L,
    			NULL );
    	if( FAILED(hr) )
    	{
    		ErrorMsg(IDS_FAILED_DISPLAY_PROPERTY_PAGES, MB_ICONERROR);
    	}
    
    	CoTucancode.netMemFree( (void*) caGUID.pElems );
    	return;
    }

    A few comments:
    - OleCreatePropertyFrame is the method to display the property pages you selected in GetPages() method before;
    - m_hWnd
    member you have because your control is derived from CWnd;
    - The fourth parameter (OLESTR string) is just the caption of your property sheet and you may type whatever you want there;
    - I use GetIDispatch(TRUE) of CCmdTarget to get a pointer to IDispatch interface of my control, but actually all you need is a pointer to IUnknown. If you already have a pointer to IUnknown of your control, just use it.

    That's it, folks! Simple, isn't it? I have no idea why it is so difficult to compile this information from multiple sources :-)

Case 1: Your container wants to display property sheet of your control in runtime mode.

Actually, as I said before, this is documented in "Inside OLE" of Brockschmidt (pp795+.) Unfortunately, Brockschmidt assumes that you create your OLE control from scratch without COleControl class. If you have already derived your control from COleControl class (as you normally do), you already have a train of interfaces which you can see in OLE Object Viewer. In this case the following steps demonstrate how to display the property pages in runtime mode in your container.

  1. Add GetPages() method to the implementation file of your control this way:
    // This method returns array of property pages used then by
    // OLE container to bring property pages to the user
    STDMETHODIMP CDoSomethingCtrl::"#630000">XSpecifyPropertyPages::GetPages(CAUUID *pPages)
    {
        GUID *pGUID;
        const unsigned CPROPPAGES = 4;
    
        pPages->cElems = 0;
        pPages->pElems = NULL;
    
        pGUID = (GUID*) CoTucancode.netMemAlloc( CPROPPAGES * sizeof(GUID) );
    
        if( NULL == pGUID )
        {
            return ResultFromScode(E_OUTOFMEMORY);
        }
    
        // Fill the array of property pages now
        pGUID[0] = COptionsPropPage::guid;
        pGUID[2] = CLSID_CFontPropPage;
        pGUID[3] = CLSID_CColorPropPage;
        pGUID[4] = CLSID_CPicturePropPage;
    
        //Fill the structure and return
        pPages->cElems = CPROPPAGES;
        pPages->pElems = pGUID;
        return NOERROR;
    }

    Pay attention to this strange class XSpecifyPropertyPages: there is no place to declare this. Where does it come from? From COleControl, of course. Declaration of COleControl class includes the following lines:

    // ISpecifyPropertyPages
    BEGIN_INTERFACE_PART(SpecifyPropertyPages, ISpecifyPropertyPages)
        INIT_INTERFACE_PART(COleControl, SpecifyPropertyPages)
        STDMETHOD(GetPages)(CAUUID*);
    END_INTERFACE_PART(SpecifyPropertyPages)
            

    where BEGIN_INTERFACE_PART is further decoded to

    #define BEGIN_INTERFACE_PART(localClass, baseClass) \ class X##localClass : public baseClass \ { \ public: \ STDMETHOD_(ULONG, AddRef)(); \ and so on

    This is where XSpecifyPropertyPages comes from.

  2. Add OnShowProperties method to the implementation file of your container (this is taken as is from the Inside OLE of BrockSchmidt with a note below):
    void CApp::OnShowProperties(void)
    {
        ISpecifyPropertyPages  *pISPP;
        CAUUID                  caGUID;
        HRESULT                 hr;
        LCID lcid = AmbientLocaleID();
    
        if (FAILED(m_pIDispatch->QueryInterface(IID_ISpecifyPropertyPages, (void **)&pISPP)))
        {
            AfxMessageBox("Object has no property pages.");
            return;
        }
    
        hr=pISPP->GetPages(&caGUID);
        pISPP->Release();
    
        if (FAILED(hr))
        {
            AfxMessageBox("Failed to retrieve property page GUIDs.");
            return;
        }
    
        hr=OleCreatePropertyFrame(m_hWnd, 10, 10, OLETEXT("Beeper")
            , 1, (IUnknown **)&m_pIDispatch, caGUID.cElems
            , caGUID.pElems, lcid, 0L, NULL);
    
        if (FAILED(hr))
            AfxMessageBox("OleCreatePropertyFrame failed.");
    
        //Free GUIDs.
        CoTucancode.netMemFree((void *)caGUID.pElems);
        return;
    }

    Notes: I changed a few minor things to get this code compiled imeediately without any further changes. I changed Message method of BrockSchmidt to the standard AfxMessageBox(). Then, I replaced the 9th parameter from m_lcid to 0L because I am not interested in locale information (of course you have to take care of it if you support multiple languages!!!)

The theory of property pages, property pages browsing and notifications is slightly more difficult than that. I sincerely encourage you to read the sixteenth chapter of "Inside OLE" of BrockSchmidt to get the full picture of OLE property pages.

 

 

Copyright ?1998-2009 UCanCode.Net Software , all rights reserved.
Other product and company names herein may be the trademarks of their respective owners.

Please direct your questions or comments to webmaster@ucancode.net