Property Pages for ActiveX Controls

14 篇文章 0 订阅
5 篇文章 0 订阅

Property Pages for ActiveX Controls
Posted by Shimon Pozin on October 1st, 2002
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:

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 on

This 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.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值