ActiveX Control Property Page Container


First I 'd like to write some reason how I come up with this idea, probably that will help future user of this code understand the power and flexibility which these functions provide.

Everybody understands the benefits of writing component software and COM comes very handy to help in implementation of components. One of great applications of COM component technology is ActiveX controls which allow to implement various kind of powerful controls and use them almost everywhere. If you implemented powerful desktop application COM (OLE2) also comes very helpful when you want to use parts of you objects within other applications or contain peace (or even entire ActiveX document) of other application.

But there is some area that for some reason is missing from component infrastructure invented by Microsoft: it is dialog boxes. Sometimes this appears very useful if for example you want to implement some sort of reporting applications where others, including you, can write separate components to provide new type of query. Usually such components must contain dialog box or property page to allow user enter query specific parameters and engine which retrieves records using parameters entered by user.

When I was faced with such problem I started thinking about ActiveX controls but controls can't contain several child windows and especially navigate focus smoothly between them as it works in dialog box. And then I though why we can't take this control property page and put it inside another dialog box or formview as a container. Actually I did not invent anything new I just used existing technology in different application. All features I used here are very well documented in Visual C++ help files but anyway I did not see any samples of such usage so I have to start from scratch.

The function showed below implements most part of the functionality. There are 2 interfaces involved: IPropertyPage and ISpecifyPropertyPages. Container dialog box implements IPropertyPageSite to host property pages within itself. I heavily use COM smart pointer declared in <comdef.h> and encourage everybody to use them because they simplify life a lot. I understand that this is not "a MFC style" but this is really boring to write all this using plain LPUNKNOWNs. Code provided here uses first page of ActiveX control to display on the container dialog box but page number can be easily changed in CoCreateInstance() call. Besides displaying property page this function can be used to switch pages on fly, provided you add user interface to select different ActiveX controls.

Property page resource in ActiveX control however requires additional style named "Control" located on "More Styles" page otherwise navigation keys like Tab and Shift+Tab will not jump from container controls to inner property page controls. The other limitation is that property page can be 250x110 or 250x62 dialog units otherwise debug version of MFC will present a message box. From my point of view this space is quite enough for most applications, and anyway you need some space limitation if you want to be able to create new pages without rewriting the container. If you ok to live with MFC debug warning message you may use whatever size you want. Happily release version of MFC doesn't produce any warnings.

This sample is some kind symbiosys because ActiveX control is running in regular mode while property page is displayed which suppose to be visible only in design mode. But it works and it appears very helpful for me and I hope for somebody else. Sample contains implementation for classes derived from CDialog but I tried them in CFormView and even dockable control bar - they all work perfectly.

BOOL CPropPageCntrDlg::SetCurrentPage( IUnknown * pUnknown, LPRECT rc )
{
	HRESULT hr = NOERROR;

	try
	{
		// hide previous page and release page pointer
		if( m_pCurrentPage != NULL )
		{
			m_pCurrentPage->Show( SW_HIDE );
			m_pCurrentPage->Deactivate();
			m_pCurrentPage = NULL;

			if( pUnknown == NULL ) return TRUE;
		}
		
		ISpecifyPropertyPagesPtr pSpecifyPropertyPages = pUnknown;

		CAUUID pages;
		hr = pSpecifyPropertyPages->GetPages( &pages );
		if( FAILED( hr ) ) throw _com_error( hr );

		ASSERT( pages.cElems > 0 && pages.pElems != NULL );

		IPropertyPagePtr pPropPage;
		
		// get 0-page GUID and create page object
		hr = CoCreateInstance( pages.pElems[0], NULL, CLSCTX_INPROC, IID_IPropertyPage, (void**)&pPropPage );
		if( FAILED( hr ) ) throw _com_error( hr );

		hr = pPropPage->SetPageSite( (IPropertyPageSite*) GetInterface( &IID_IPropertyPageSite ) );
		if( FAILED( hr ) ) throw _com_error( hr );

		hr = pPropPage->SetObjects( 1, &pUnknown );
		if( FAILED( hr ) ) throw _com_error( hr );

		hr = pPropPage->Activate( GetSafeHwnd(), rc, TRUE );
		if( FAILED( hr ) ) throw _com_error( hr );

		hr = pPropPage->Show( SW_SHOW );
		if( FAILED( hr ) ) throw _com_error( hr );

		m_pCurrentPage = pPropPage;
	}
	catch( _com_error &e )
	{
		hr = e.Error();
		ASSERT( SUCCEEDED( hr ) );
	}

	return SUCCEEDED( hr );
}

The demo project contains sample container and two dummy ActiveX controls that are generated by MFC and ATL wizards. The only option important is to mark these controls as invisible at runtime. MFC property page is more preferable if you want to put other ActiveX control on that page because up to now ATL property page does not support hosting of ActiveX controls. Sample container switches user interface on fly using ActiveX controls property pages. And what is more important container may execute queries without knowing anything about what kind of user interface required to enter query parameters! The ActiveX controls in turn talk back to container using regular fire event mechanism and container displays results of a query also without knowing how they were retrived!

Download demo project - 40 KB