Property Pages for ActiveX Controls

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*) CoTaskMemAlloc( 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);
    	}
    
    	CoTaskMemFree( (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*) CoTaskMemAlloc( 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.
        CoTaskMemFree((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.



Comments

  • How can I close the property page?

    Posted by newnick on 06/30/2005 10:49am

    Hi,
      Nice code.! 
    
     I would like to know, how can I close a property page window programmatically.?
    
     I am using OleCreatePropertyFrame(...) to invoke the property page.
    
    Thank You so much..
    
    Regards,
    Nick

    Reply
  • removing combo from font pag

    Posted by Legacy on 04/29/2003 12:00am

    Originally posted by:

    hi,
    I have a problem.The stock property page for font by deafut shows the property name as font in the combo box.IS there any way of acing this page to remove that combo box opr its part of the microsoft standard fint page.
    do need expaltion and solution
    regards

    Reply
  • As a Adviser

    Posted by Legacy on 11/24/2002 12:00am

    Originally posted by: Ramshankar

    I really lime code guru because this will be useful for learners, but my suggession is maitain like a wizard.
    what ever the prg. U supplied is very nice, but just diveded into steps(i.e step wise, what is the next step after that...).

    I hope this type of prg. will be usefull for everyone and easy to do.

    thanking U sir,

    Reply
  • ActiveX Control...

    Posted by Legacy on 10/01/2001 12:00am

    Originally posted by: Philip

    Good day! i am now creating my own activex control for dialogic product using "mfc activex controlwizard" project. im concern with the simplicity but i dont know what best project im going to use. can it be possible to use other projects such as win32, atl com in creating my ocx?
    
    

    so far, i have some difficulties in "Add Method" specifically in return values data types....specially in "VARIANT" i want to use it but it seems difficult to understand, can you give me sample codes of your for me to learn it easily, please help me in developing my control!

    i want to return a list of strings from my controls to my application in vb......... but im not that expert as you are. hoping for your answer. thanks!

    philip
    philippines

    Reply
  • Property page Question, How do I make a property page *into* my control?

    Posted by Legacy on 07/12/2001 12:00am

    Originally posted by: David Sowsy

    I'm still relatively new to ActiveX programming. 
    
    

    I've gone through many tutorials, and read a few
    of the major ActiveX FAQs. I'm using ActiveX in VC++.

    I was wondering is there a *simple* way of telling
    my control that I want to see the Properties page instead
    of the ActiveX control that throws me back into the stone
    ages of mucking with a pdc and forcing me to default
    to an inane ellipse?

    Reply
  • Can't understand this...

    Posted by Legacy on 10/04/1999 12:00am

    Originally posted by: Grant Gussie

    My browser displays the first line of the GetPages method as

    STDMETHODIMP CDoSomethingCtrl::"#630000">GetPages(CAUUID *pPages)

    which doesn't look like any C++ I know. What is "#630000"?

    Also, how do you declare this in CDoSomethingCtrl's header file? It won't compile with anything I tried. How 'bout a zipped sample project that compiles on version 6?

    Reply
  • Showing/Hiding property pages at runtime

    Posted by Legacy on 02/14/1999 12:00am

    Originally posted by: Robert Fairlie

    This comes close to solving a problem which I have. I am developing an ActiveX control which has a list box
    on the first property page to which the user adds items. If there are no items entered the other property
    pages do not apply, and it could be confusing if the user goes to them and nothing happens when he sets
    properties. Do you know if it is possible to just display the first page when the property frame is first
    invoked, and programmatically add the other pages when the user has entered an item?
    
    Robert.

    Reply
  • Memory leaks when displaying property sheet at runtime

    Posted by Legacy on 10/08/1998 12:00am

    Originally posted by: Raphael Second

    I use the GetPages() and OnShowProperties() methods for display property sheet of my ActiveX in runtime mode. It's very useful except I have memory leaks.
    So I have add the line "pIDispatch->Release();" at the end of the OnShowProperties method. I have no more memory leaks.

    Reply
Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • Today's agile organizations pose operations teams with a tremendous challenge: to deploy new releases to production immediately after development and testing is completed. To ensure that applications are deployed successfully, an automatic and transparent process is required. We refer to this process as Zero Touch Deployment™. This white paper reviews two approaches to Zero Touch Deployment--a script-based solution and a release automation platform. The article discusses how each can solve the key …

  • On-demand Event Event Date: December 18, 2014 The Internet of Things (IoT) incorporates physical devices into business processes using predictive analytics. While it relies heavily on existing Internet technologies, it differs by including physical devices, specialized protocols, physical analytics, and a unique partner network. To capture the real business value of IoT, the industry must move beyond customized projects to general patterns and platforms. Check out this webcast and join industry experts as …

Most Popular Programming Stories

More for Developers

RSS Feeds