dcsimg

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


This article was originally published on December 7th, 2000

Most Popular Programming Stories

More for Developers

RSS Feeds

Thanks for your registration, follow us on our social networks to keep up-to-date