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
250×110 or 250×62 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

More by Author

Must Read