Changing Contents of a Property Page with a ComboBox

I wanted to create a property page whose controls changed when the user changed the
selection in a combo box located on the page. This property page is similar to the
one in Microsoft Visual Studio on the Project…Settings…C/C++ tab. On that page when
you change the selection in the Category combo box, all of the controls beneath the
combo box change. For example,


My first reaction was to create a dialog in the resource editor with the controls stacked
on top of each other and show/hide the various controls. This for obvious reasons is a
maintenance nightmare. What I really wanted to do was create separate dialogs for each
option in the combo box. Thanks to Mike Liss’s CodeGuru article on modeless child dialogs
not only did I learn how to do just that but also how to get the tab order to go from the
controls on the property page to the embedded dialog’s controls properly.

Instead of deriving the child dialogs from CDialog, I instead chose to derive them from
CPropertyPage. My reason for doing this is so that the main property page could invoke
the CPropertyPage methods in the child pages. For example, in OnKillActive the main page
would invoke OnKillActive on the current child page to determine whether it was acceptable
for the main page to no longer be the active page.

Step 1: Create the main property page in the resource editor

Open the resource editor and add a new dialog. Assign an appropriate ID and set the caption
for the property sheet tab. Remove the OK and Cancel buttons. Drop static text on the page
to describe the contents of the combo box. Now drop the combo box on the page. Bring up
the properties for the combo box and set the ID to IDC_COMBOPAGE_COMBO. Switch to the
Styles tab and change the Type to Drop List and remove the checkmark for Sort. For example,

Do NOT fill in any data for the combo box.

Now drop a Picture control on the page and position it as far left as possible. You do
not need to resize the control. Bring up the properties for the picture and set the ID
to IDC_DLG_AREA. Clear the Visible checkbox and make sure the Type is set to Frame.
For example,

Your dialog/property page should look like the following:

Step 2: Create the child property pages

Follow these steps for each option that you wish to appear in the combo box. Insert a new
dialog in the resource editor. Assign an appropriate ID and set the caption to the text
that you wish to appear in the combo box. Drop whatever controls you wish onto your
dialog. Bring up the Properties for the dialog and select the Styles tab. Make it a child
dialog with a thin border on this page, such as:

Select the More Styles tab and check the Control checkbox, for example:

Create a class for the property page by bringing up the ClassWizard. Make sure the class
is derived from CPropertyPage. Add a handler for WM_INITDIALOG if you wish to initialize
the controls in your property page. If you want to validate the data in your property
pages, write a handler for OnKillActive and return FALSE if the page is invalid.

Step 3: Add a new class to your project

After you have downloaded the source code, copy the CodePage.cpp and CodePage.h files to
your project directory and add them to your project. Be sure to include the CodePage.h
file from the source file that will add the page to a property sheet. Also include all
of the header files for the property page classes to add to the combo page.

Step 4: Add the property pages to the page

In the source file that will use the combo page, you will need to create an instance of
the combo page and all of the property pages to add to it. When creating an instance of
CComboPage you need to specify the ID of the property page you created in step 1. Then you
simply add the property pages to the combo page specifying the ID of each property page.
For example,


void CComboWizardView::OnViewComboDialog()
{
CPropertySheet sheet;
CFirstPage firstPage;
CLastPage lastPage;

CComboPage comboPage( IDD_COMBOPAGE );
COptionAPage pageA;
COptionBPage pageB;
COptionCPage pageC;

// Add the pages to the combo page
comboPage.AddPage( &pageA, pageA.IDD );
comboPage.AddPage( &pageB, pageB.IDD );
comboPage.AddPage( &pageC, pageC.IDD );

// Add the property pages to the property sheet
sheet.AddPage( &firstPage );
sheet.AddPage( &comboPage );
sheet.AddPage( &lastPage );

// Set the title of the property sheet window
sheet.SetTitle( “Property Sheet Containing a ComboPage” );

// Uncomment the next line to display a wizard instead.
// sheet.SetWizardMode();

sheet.DoModal();
}

Step 5: Process the results

If you are using the combo property page to obtain a variety of information on a single
topic, such as the C/C++ tab in Visual Studio, then you will want to obtain all of the
information from each of the property pages. If you need to perform this step after the
call to DoModal(), then use the local variables for each property page that were created
with the property sheet. If you need to perform this step in the combo page code or if
you are passing the combo page to another routine to do this, you can get each property
page by calling CComboPage::GetPage and specifying the index of the page to retrieve.

If you are using the combo property page to have the user select and configure a single
option, then you can use the CComboPage::GetSelectedPage to get the page the user
selected through the combo box. This method returns a pointer to the page and the ID
of the page. For example,


UINT nID;
CPropertyPage* pPage = comboPage.GetSelectedPage( nID );

switch ( nID )
{
case pageA.IDD:
{
CString string;
COptionAPage* pOptionAPage = (COptionAPage*) pPage;
int nRadio = pOptionAPage->m_radio;
string.Format(“You selected option A and radio %d”,
nRadio + 1 );
::AfxMessageBox( string );
break;
}

case pageB.IDD:
::AfxMessageBox( “You selected option B” );
break;

case pageC.IDD:
::AfxMessageBox( “You selected option C” );
break;
}

Final Notes:

If you need to enable the Apply button when the data in your property page changes, calling
SetModified will not work since the child property pages do not belong to the property
sheet. Instead you will need to get the parent window, cast it to a property page, and
then call SetModifed on the parent. For example,


void COptionAPage::OnChange()
{
CPropertyPage* pParent = (CPropertyPage*) GetParent();
pParent->SetModified();
}

You can still override the CPropertyPage methods in your derived classes. For example,
OnOK will be called on every child page when the OK button is pressed. OnKillActive is
called on the current page when the user changes the setting in the combo box and
OnSetActive is called on the new page. UpdateData is called on any page before
OnKillActive is called so you do not need to perform this step. Also when the main
property page is no longer the active page, it will invoke OnKillActive on the current
page. The OnWizardBack, OnWizardNext, and OnWizardFinish methods are the only other
methods that are invoked only on the currently selected page.

Downloads

Download demo project – 30 Kb

Download source – 4 Kb

More by Author

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Must Read