Chapter 9

Property Pages and Sheets and Wizards


CONTENTS

One of the newest types of graphical objects is the tabbed dialog box, also known as a property sheet. Windows 95 is loaded with property sheets, which organize the many options that can be modified by the user. What's a property sheet? Basically, it's a dialog box with two or more pages. You flip the pages by clicking labeled tabs located at the top of the dialog box. By using such dialog boxes to organize complex groups of options, Windows 95 enables users to find more easily the information and settings that they need. As you've probably guessed, Visual C++ 5.0 supports the Windows 95 property sheets, with the classes CPropertySheet and CPropertyPage.

Similar to property sheets are wizards, which use buttons to move from one page to another rather than using tabs. You've seen a lot of wizards, too. These special types of dialog boxes guide the user step-by-step through complicated processes. For example, when you use AppWizard to generate source code for a new project, the wizard guides you through the entire process. To control the wizard, you click buttons labeled Back, Next, and Finish.

Introducing Property Sheets

Finding a sample property sheet in Windows 95 is as easy as finding sand at the beach. Just click virtually any Properties command or double-click an icon in the Control Panel. For example, Figure 9.1 shows the dialog box that you see when you double-click the Control Panel's Add/Remove Programs icon. This is a property sheet that contains three pages labeled Install/Uninstall, Windows Setup, and Startup Disk, each page containing commands and options related to the page's title topic.

Figure 9.1 : The Add/Remove Programs Properties sheet contains three tabbed pages.

In Figure 9.1, you can see programs installed on the machine that Windows can automatically uninstall. There's also an Install button that leads to other dialog boxes that help you install new programs from floppy disk or CD-ROM. On the other hand, the Windows Setup page (Figure 9.2) helps you add or remove files from the Windows system. To get to this page, you need only click the Windows Setup tab. The Startup Disk page, of course, houses yet another set of options.

Figure 9.2 : To move to the Windows Setup page, you click the Windows Setup tab.

As you can see, property sheets are a great way to organize many types of related options. Gone are the days of dialog boxes so jam-packed with options that you needed a college-level course just to figure them out. In the sections that follow, you will learn to program your own tabbed property sheets using MFC's CPropertySheet and CPropertyPage classes.

Creating the Property Sheet Demo Application

Now that you've had an introduction to property sheets, it's time to learn how to build an application that uses these handy specialized dialog boxes. In the steps that come later, you'll build the Property Sheet Demo application, which demonstrates the creation and manipulation of property sheets. Follow the steps that come next to create the basic application and modify its resources.

NOTE
The complete source code and executable file for the Property Sheet application can be found in the CHAP09\PSHT directory of this book's CD-ROM.

  1. Use AppWizard to create the basic files for the Property Sheet Demo program, selecting the options listed in the following table. When you're done, the New Project Information dialog box appears; it should look like Figure 9.3. Click the OK button to create the project files.
    Figure 9.3 : Your Project Information dialog box should look like this.

Dialog Box NameOptions to Select
New ProjectName the project psht and then set the project path to the directory into which you want to store the project's files. Leave the other options set to their defaults.
Step 1Select Single Document.
Step 2 of 6Leave set to defaults.
Step 3 of 6Leave set to defaults.
Step 4 of 6Turn off all application features.
Step 5 of 6Leave set to defaults.
Step 6 of 6Leave set to defaults.

  1. Select the ResourceView tab in the project workspace window. Visual C++ displays the ResourceView window, as shown in Figure 9.4.
    Figure 9.4 : The ResourceView tab displays the ResourceView window.
  2. In the ResourceView window, click the plus sign next to psht resources to display the application's resources. Click the plus sign next to Menu and then double-click the IDR_MAINFRAME menu ID. Visual C++'s menu editor appears.
  3. Click on the Property Sheet Demo application's Edit menu (not Visual C++'s Edit menu) and then press your keyboard's Delete key to delete the Edit menu. When you do, a dialog box asks for verification of the delete command. Click the OK button.
  4. Double-click the About psht... item in the Help menu and change it to About Property Sheet Demo.
  5. In the application's File menu, delete all menu items except Exit.
  6. Add a Property Sheet item to the File menu, giving it the command ID ID_PROPSHEET, as shown in Figure 9.5. Then use your mouse to drag the new command above the Exit command, so that it's the first command in the File menu.
    Figure 9.5 : Add a Property Sheet command to the File menu.
  7. Double-click the Accelerator resource in the ResourceView window and highlight the IDR_MAINFRAME accelerator ID. Press your Delete key to delete all accelerators from the application.
  8. Double-click the Dialog resource in the ResourceView window. Double-click the IDD_ABOUTBOX dialog box ID to bring up the dialog box editor.
  9. Modify the dialog box by changing the title to "About Property Sheet Demo," changing the first static text string to "Property Sheet Demo, Version 1.0," and adding the static string "by Macmillan Computer Publishing," as shown in Figure 9.6. Close the dialog box editor.
    Figure 9.6 : The About the box should look like this.
  10. Double-click the String Table resource in the ResourceView window. Double-click the String Table ID to bring up the string table editor.
  11. Double-click the IDR_MAINFRAME string and then change the first segment of the string to "Property Sheet Demo," as shown in Figure 9.7. Close the string table editor.
    Figure 9.7 : The first segment of the IDR_MAINFRAME string appears in your main window's title bar.

Now that you have the application's basic resources the way you want them, it's time to add the resources that define the application's property sheet. This means creating dialog box resources for each page in the property sheet. Follow the next steps to complete this task.

  1. Click the New Dialog button in Developer Studio's toolbar, or press Ctrl+1 on your keyboard, to create a new dialog resource. The new dialog box appears in the dialog box editor.
    This dialog box, when properly set up, will represent the first page of the property sheet.
  2. Delete the OK and Cancel buttons by selecting each with your mouse and then pressing your keyboard's Delete key.
  3. Double-click the dialog box to bring up its Dialog Properties sheet. Change the ID to IDD_PAGE1DLG and the caption to Page 1, as shown in Figure 9.8.
    Figure 9.8 : Double-clicking the dialog box brings up its properties.
  4. Click the Styles tab. Change the St_yle box to Child, the Border box to Thin, and turn off the System Menu check box, as shown in Figure 9.9.
    The Child style is necessary because the property page will be a child window of the property sheet. The property sheet itself will provide the container for the property pages. (Actually, if you forget to set these styles, MFC seems to be smart enough to display the property page properly, in spite of your oversight.)
    Figure 9.9 : A property page uses different styles than those in a regular dialog box.
  5. Add an edit box to the property page, as shown in Figure 9.10.
    Figure 9.10 : A property page can hold whatever controls you like.
  6. Create a second property page by following the previous steps 1 through 5. For this property page, use the ID IDD_PAGE2DLG, a caption of Page 2, and add a check box rather than an edit control, as shown in Figure 9.11.
    Figure 9.11 : The second property page should look like this.

You now have all your resources created. However, you need to associate your two new property-page resources with C++ classes so that you can control them in your program.You also need a class for your property sheet, which will hold the property pages that you've created. Follow the steps given next to create the new classes.

  1. Make sure that the Page 1 property page is visible in the dialog edit box and then either click the Developer Studio's ClassWizard button or select View, ClassWizard from the menu bar. The MFC ClassWizard property sheet appears, displaying the Adding A Class dialog box.
  2. Select the Create New Class option and then click the OK button. The Create New Class dialog box appears.
  3. In the Name box, type CPage1, and in the Base Class box, select CPropertyPage. Then click the Create button to create the class.
    You've now associated the property page with an object of the CPropertyPage class, which means that you can use the object to manipulate the property page as needed. The CPropertyPage class will be especially important when you learn about wizards.
  4. Select the Member Variables tab of the MFC ClassWizard property sheet. With IDC_EDIT1 highlighted, click the Add Variable button. The Add Member Variable dialog box appears.
  5. Name the new member variable m_edit, as shown in Figure 9.12, and then click the OK button. ClassWizard adds the member variable to the new CPage1 class.
    The member variable will hold the value of the property page's control
    Figure 9.12 : ClassWizard makes it easy to add member variables.

    .

  6. Click OK on the MFC ClassWizard Properties sheet to finalize the creation of the CPage1 class.
  7. Follow steps 1 through 7 for the second property sheet. Name the class CPage2 and add a Boolean member variable called m_check for the IDC_CHECK1 control, as shown in Figure 9.13.
    Figure 9.13 : The second property page needs a Boolean member variable called m_checkbox.

  8. Close all the resource editor windows. Then either click the ClassWizard button or select View, ClassWizard from the menu bar. The MFC ClassWizard Properties sheet reappears.
  9. Select New from the Add Class menu button. The Create New Class dialog box appears.
  10. In the Name box, type CPropSht, select CPropertySheet in the Base Class box, and then click the Create button. ClassWizard creates the CPropSht class. Click the MFC ClassWizard Properties sheet's OK button to finalize the class.
    In the finished application, you'll use the new CPropSht class to create and manipulate the property sheet.

At this point, you have three new classes-CPage1, CPage2, and CPropSht-in your program. The first two classes are derived from MFC's CPropertyPage class, and the third is derived from CPropertySheet. Although ClassWizard has created the basic source-code files for these new classes, you still have to add code to the classes to make them work the way you want. Follow the next set of steps to complete the Property Sheet Demo application.

  1. Click the FileView tab in order to display the FileView window. Then display the project's files by clicking the plus sign next to the folder labeled "psht files," as shown in Figure 9.14.
    Figure 9.14 : The FileView window lists the source files that make up your project.

  2. Double-click the PropSht.cpp entry in the file list. The PROPSHT.CPP file appears in the code window. Click the Open Header button (the button with the ".h" on it) to display the class's header file.
  3. Add the following lines near the top of the file, right before the beginning of the class's declaration:
    #include "page1.h"
    #include "page2.h"
    These lines give the CPropSht class access to the CPage1 and CPage2 classes, so that the property sheet can declare member variables of these property page classes.
  4. Add the following lines to the CPropSht class's attributes section, right after the public keyword:
    CPage1 m_page1;
    CPage2 m_page2;
    These lines declare the class's data members, which are the property pages that'll be displayed in the property sheet.
  5. Close the PROPSHT.H file (saving the changes) so that you can view and edit the PROPSHT.CPP file. Add the following lines to the class's second constructor:
    AddPage(&m_page1);
    AddPage(&m_page2);
    The preceding lines add the two property pages to the property sheet when the sheet is constructed.
  6. Open the PSHTVIEW.CPP file and then click the Open Header button to display the PSHTVIEW.H file. Add the following lines to the class's Attributes section, right after the line CPshtDoc* GetDocument();
    protected:
    CString m_edit;
    BOOL m_check;
    These lines declare two data members for the view class. These data members will hold the selections made in the property sheet by the user.
  7. Close the PSHTVIEW.H file (saving the changes) so you can edit the PSHTVIEW.CPP file. Add the following line near the top of the file, after the #endif compiler directive:
    #include "propsht.h"
    This line gives the view class access to the CPropSht class, so that it can create the property sheet when requested to do so.
  8. Add the following lines to the view class's constructor:
    m_edit = "Default";
    m_check = FALSE;
    These lines initialize the class's data members so that, when the property sheet appears, these default values can be copied into the property sheet's controls. After the user changes the contents of the property sheet, these data members will always hold the last values from the property sheet, so those values can be restored to the sheet when needed.
  9. Add the lines shown in Listing 9.1 to the OnDraw() function, right after the TODO: add draw code for native data here comment.

Listing 9.1  LST9_1.TXT-Code for the OnDraw() Function

pDC->TextOut(20, 20, m_edit);

if (m_check)

pDC->TextOut(20, 50, "TRUE");

else

pDC->TextOut(20, 50, "FALSE");



    These lines display the current selections from the property sheet. At the start of the program, the default values are displayed.
  1. Click the toolbar's ClassWizard button or select the View, ClassWizard command from the menu bar. The ClassWizard property sheet appears.
  2. Make sure that CPshtView is selected in the Class Name and Object IDs boxes. Then add the OnPropsheet() message-response function, as shown in Figure 9.15.
    The OnPropsheet() function is now associated with the Property Sheet command that you previously added to the File menu. That is, when the user selects the Property Sheet command, MFC calls OnPropsheet(), where you can respond to the command.
    Figure 9.15 : Use ClassWizard to add the OnPropsheet() member function.

  3. Click the Edit Code button to jump to the OnPropsheet() function, and then add the lines shown in Listing 9.2 right after the TODO: Add your command handler code here comment.

Listing 9.2  LST9_2.TXT-Code for the OnPropSheet() Function

CPropSht propSheet("Property Sheet", this, 0);

propSheet.m_page1.m_edit = m_edit;

propSheet.m_page2.m_check = m_check;

int result = propSheet.DoModal();

if (result == IDOK)

{

    m_edit = propSheet.m_page1.m_edit;

    m_check = propSheet.m_page2.m_check;

    Invalidate();

}



    The code segment in Listing 9.2, which is discussed in more detail a little later in this chapter, creates, displays, and manages the property sheet.

You've now finished the complete application. Click the toolbar's Build button, or select the Build command from the menu bar, to compile and link the application.

Running the Property Sheet Demo Application

Once you have the program compiled, run it. When you do, you see the window shown in Figure 9.16. As you can see, the window displays two values, which are the default values for the controls in the application's property sheet. You can change these values using the property sheet. To do this, select the File menu's Property Sheet command. The property sheet appears on the screen (Figure 9.17). The property sheet contains two pages, each of which holds a single control. When you change the settings of these controls and click the property sheet's OK button, the application's window displays the new values.
Figure 9.16 : When it first starts, the Property Sheet Demo application displays default values for the property sheet's controls.


Figure 9.17 : The application's property sheet contains two pages.

Understanding the Property Sheet Demo Application

Previously, in the section titled "Creating the Property Sheet Demo Application," you went through the process of creating Property Sheet Demo, step-by-step. During this process, you discovered that you must complete several tasks in order to add property sheets to your application. Each of those steps was explained in the step's text. However, to give you a clearer picture of what you did, the steps that are most important in the creation of a property sheet are summarized here:

  1. Create a dialog box resource for each page in the property sheet. These resources should have the Child and Thin styles and should have no system menu.
  2. Associate each property page resource with an object of the CPropertyPage class. You can do this easily with ClassWizard.
  3. Create a class for the property sheet, deriving the class from MFC's CPropertySheet class. You can generate this class using ClassWizard.
  4. In the property sheet class, add member variables for each page you'll be adding to the property sheet. These member variables must be instances of the property page classes that you created in Step 2.
  5. In the property sheet's constructor, call AddPage() for each page in the property sheet.
  6. To display the property sheet, call the property sheet's constructor and then call the property sheet's DoModal() member function, just as you would with a dialog box.

NOTE
As you read over the steps required for creating a property sheet, be sure that you understand the difference between a property sheet and a property page. A property sheet is a window that contains property pages. Property pages are windows that hold the controls that will appear on the property sheet's pages.

After you have your application written and have defined the resources and classes that represent the property sheet (or sheets; you can have more than one), you need a way to enable the user to display the property sheet when it's needed. In Property Sheet Demo, this is done by associating a menu item with a message-response function. However you handle the command to display the property sheet, though, the process of creating the property sheet is the same. First, you must call the property sheet class's constructor, which Property Sheet Demo does like this:


CPropSht propSheet("Property Sheet", this, 0);

Here, the program is creating an instance of the CPropSht class. This instance (or object) is called propSheet. The three arguments are the property sheet's title string, a pointer to the parent window (which, in this case, is the view window), and the zero-based index of the first page to display. Because the property pages are created in the property sheet's constructor, creating the property sheet also creates the property pages.

Once you have the property sheet object created, you can initialize the data members that hold the values of the property page's controls, which Property Sheet Demo does like this:


propSheet.m_page1.m_edit = m_edit;

propSheet.m_page2.m_check = m_check;

Now it's time to display the property sheet on the screen, which you do just as if it were a dialog box, by calling the property sheet's DoModal() member function:


int result = propSheet.DoModal();

DoModal() doesn't take any arguments, but it does return a value indicating which button the user clicked to exit the property sheet. In the case of a property sheet or dialog box, you'll usually want to process the information entered into the controls only if the user clicked the OK button, which is indicated by a return value of IDOK. Listing 9.3 shows how the Property Sheet Demo application handles the return value:


Listing 9.3  LST9_3.TXT-Handling the Property Sheet's Return Value

if (result == IDOK)

{

    m_edit = propSheet.m_page1.m_edit;

    m_check = propSheet.m_page2.m_check;

    Invalidate();

}


In Listing 9.3, the program retrieves the values of the controls from the property pages and then calls Invalidate() to force the window to be redrawn. If the user exits the property sheet by clicking the Cancel button, the code in the body of the if statement is ignored and the window is not updated.

Changing Property Sheets to Wizards

When you come right down to it, a wizard is nothing more than a property sheet that uses Back, Next, and Finish buttons instead of tabs. Because of the lack of tabs, however, the user must switch from one page to another in sequence. This forced sequence makes wizards terrific for guiding your application's users through the steps needed to complete a complex task. You've already seen how AppWizard in Visual C++ makes it easy to start a new project. You can create your own wizards that are suited to whatever application you want to build. In the following sections, you'll see how easy it is to convert a property sheet to a wizard.

Running the Wizard Demo Application

In the CHAP09\WIZ folder of this book's CD-ROM, you'll find the Wizard Demo application. This application was built in much the same way as the Property Sheet Demo application that you created earlier in this chapter. However, as you'll soon see, there are a few differences in the Wizard Demo application that enable the user to access and use the application's wizard.

When you run the Wizard Demo application, the main window appears, including a File menu from which you can select the Wizard command. The Wizard command brings up the wizard shown in Figure 9.18.
Figure 9.18 : The Wizard Demo application displays a wizard rather than a property sheet.

The wizard isn't too fancy, but it does demonstrate what you need to know in order to program more complex wizards. As you can see, this wizard has three pages. On the first page is an edit control and three buttons called Back, Next, and Cancel. The Back button is disabled, because there is no previous page to go back to. The Cancel button enables the user to dismiss the wizard at any time, canceling whatever process the wizard was guiding the user through. The Next button causes the next page in the wizard to be displayed.

You can change whatever is displayed in the edit control if you like. However, the magic really starts when you click the Next button, which displays Page 2 of the wizard, as shown in Figure 9.19. Page 2 contains a check box and the Back, Next, and Cancel buttons. Now, the Back button is enabled, so that you can return to Page 1 if you want to. Go ahead and click the Back button. The wizard tells you that the check box must be checked, as shown in Figure 9.20. As you'll soon see, this feature of a wizard enables you to verify the contents of a specific page before allowing the user to advance to another step.
Figure 9.19 : In Page 2 of the wizard, the Back button is enabled.

Figure 9.20 : You must select the check box before the wizard will let you leave Page 2.

After checking the check box, you can click the Back button to move back to Page 1 or click the Next button to advance to Page 3. Assuming you advance to Page 3, you see the display shown in Figure 9.21. Here, the Next button has changed to the Finish button, because you are on the wizard's last page. If you click the Finish button, the program displays a message box, after which the wizard disappears.

Figure 9.21 : This is the last page of the Wizard Demo Application's wizard.

Creating Wizard Pages

As far as your application's resources go, you create wizard pages exactly as you create property sheet pages, by creating dialog boxes and changing the dialog box styles. You also need to associate each page that you create with an object of the CPropertyPage class. However, in order to take control of the pages in your wizard and keep track of what the user is doing with the wizard, there are a couple of member functions in the CPropertyPage class that you can override in your property page classes. These functions are OnSetActive(), OnWizardBack(), OnWizardNext(), and OnWizardFinish(). Read on to see how to use these functions.

Setting the Wizard's Buttons

MFC automatically calls the OnSetActive() member function immediately upon displaying a specific page of the wizard. For example, when the program displays Page 1 of the wizard, the CPage1 class's OnSetActive() function gets called. In the Wizard Demo application, the CPage1 class's version of OnSetActive() looks like Listing 9.4.


Listing 9.4  LST9_4.TXT-The OnSetActive() Member Function

BOOL CPage1::OnSetActive() 

{

    // TODO: Add your specialized code here and/or call the base class

    CPropertySheet* parent = (CPropertySheet*)GetParent();

    parent->SetWizardButtons(PSWIZB_NEXT);

    return CPropertyPage::OnSetActive();

}


In Listing 9.4, the program first gets a pointer to the wizard's property sheet window, which is the page's parent window. Then the program calls the wizard's SetWizardButtons() function, which determines the state of the wizard's buttons. SetWizardButtons() takes a single argument, which is a set of flags indicating how the page should display its buttons. These flags are PSWIZB_BACK, PSWIZB_NEXT, PSWIZB_FINISH, and PSWIZB_DISABLEDFINISH. The button flags that you include will enable the associated button (except for the PSWIZB_DISABLEDFINISH flag, which disables the Finish button). Because the call to SetWizardButtons() in Listing 9.4 includes only the PSWIZB_NEXT flag, only the Next button in the page will be enabled.

Because the CPage2 class represents Page 2 of the wizard, its call to SetWizardButtons() enables both the Back and Next buttons, by ORing together the appropriate flags, like this:


parent->SetWizardButtons(PSWIZB_BACK | PSWIZB_NEXT);

Because Page 3 of the wizard is the last page, the CPage3 class calls SetWizardButtons(), like this:


parent->SetWizardButtons(PSWIZB_BACK | PSWIZB_FINISH);

This set of flags enables the Back button and changes the Next button to the Finish button.

Responding to the Wizard's Buttons

In the simplest case, MFC takes care of everything that needs to be done in order to flip from one wizard page to the next. That is, when the user clicks a button, MFC springs into action and performs the Back, Next, Finish, or Cancel command. However, you'll often want to perform some action of your own when the user clicks a button. For example, you may want to verify that the information that the user entered into the currently displayed page is correct. If there's a problem with the data, you can force the user to fix it before moving on.

To respond to the wizard's buttons, you can override the OnWizardBack(), OnWizardNext(), and OnWizardFinish() member functions. When the user clicks a wizard button, MFC calls the matching function in which you can do whatever is needed to process that page. An example is the way the wizard in the Wizard Demo application won't let you leave Page 2 until you've checked the check box. This is accomplished by overriding the functions shown in Listing 9.5.


Listing 9.5  LST9_5.TXT-Responding to Wizard Buttons

LRESULT CPage2::OnWizardBack() 

{

    // TODO: Add your specialized code here and/or call the base class

    CButton *checkBox = (CButton*)GetDlgItem(IDC_CHECK1);

    if (!checkBox->GetCheck())

    {

        MessageBox("You must check the box.");

        return -1;

    }

    return CPropertyPage::OnWizardBack();

}

LRESULT CPage2::OnWizardNext() 

{

    // TODO: Add your specialized code here and/or call the base class

    CButton *checkBox = (CButton*)GetDlgItem(IDC_CHECK1);

    if (!checkBox->GetCheck())

    {

        MessageBox("You must check the box.");

        return -1;

    }

    return CPropertyPage::OnWizardNext();

}


In the functions in Listing 9.5, the program gets a pointer to the page's check box by calling the GetDlgItem() function. With the pointer in hand, the program can call the check-box class's GetCheck() function, which returns a 1 if the check box is checked. If GetCheck() returns a 0, the program displays a message box and returns Ð1 from the function. Returning Ð1 tells MFC to ignore the button click and not change pages.

Displaying a Wizard

As you've just learned, almost all the work involved in controlling a wizard is done in the classes that represent the wizard's pages. The property sheet class that represents the wizard works exactly the same as it did in the property sheet example. However, there is one extra thing you must do when displaying a wizard, which is to call the property sheet's SetWizardMode() member function. This function call tells MFC that it should display the property sheet as a wizard rather than as a conventional property sheet. Listing 9.6 shows the view class's OnWizard() member function, which is the function that responds to the File menu's Wizard command.


Listing 9.6  LST9_6.TXT-Displaying a Property Sheet as a Wizard

void CWizView::OnWizard() 

{

    // TODO: Add your command handler code here

    

    CWizSheet wizSheet("Sample Wizard", this, 0);

    wizSheet.m_page1.m_edit = m_edit;

    wizSheet.m_page2.m_check = m_check;

    wizSheet.SetWizardMode();

    int result = wizSheet.DoModal();

    if (result == ID_WIZFINISH)

    {

        m_edit = wizSheet.m_page1.m_edit;

        m_check = wizSheet.m_page2.m_check;

    }

}


Notice in Listing 9.6 that the program creates the wizard almost exactly the same as a property sheet, the only difference being the call to SetWizardMode(). The wizard is displayed exactly the same as any other dialog box, by calling the object's DoModal() member function. There is, however, one difference in how you respond to the result returned by DoModal(). Because a wizard has no OK button, DoModal() cannot return IDOK. Instead, DoModal() returns ID_WIZFINISH if the user exits via the Finish button.

From Here…

Whether you're creating property sheets or wizards, Visual C++'s many classes enable you to get the job done easily. Property sheets are great for organizing many options and controls, whereas wizards (which are a special type of property sheet) are best used for guiding the user step-by-step through a complex task. To learn more about related topics, check out the following chapters: