Property Pages for ActiveX Controls
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:
- 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
- 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?)
- 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 } - 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:
- 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.
- 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.
- 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 onThis is where XSpecifyPropertyPages comes from.
- 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:49amremoving combo from font pag
Posted by Legacy on 04/29/2003 12:00amOriginally posted by:
hi,
ReplyI 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
As a Adviser
Posted by Legacy on 11/24/2002 12:00amOriginally 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,
ReplyActiveX Control...
Posted by Legacy on 10/01/2001 12:00amOriginally posted by: Philip
ReplyProperty page Question, How do I make a property page *into* my control?
Posted by Legacy on 07/12/2001 12:00amOriginally posted by: David Sowsy
ReplyCan't understand this...
Posted by Legacy on 10/04/1999 12:00amOriginally 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?
ReplyShowing/Hiding property pages at runtime
Posted by Legacy on 02/14/1999 12:00amOriginally posted by: Robert Fairlie
ReplyMemory leaks when displaying property sheet at runtime
Posted by Legacy on 10/08/1998 12:00amOriginally 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.
ReplySo I have add the line "pIDispatch->Release();" at the end of the OnShowProperties method. I have no more memory leaks.