About Property Sheets

A property sheet is a window that allows the user to view and edit the properties of an item. For example, a spreadsheet application can use a property sheet to allow the user to set the font and border properties of a cell or to view and set the properties of a device, such as a disk drive, printer, or mouse.

This section discusses the following topics.

Property Sheet Basics

To implement property sheets in your application, include the Prsht.h header file in your project. Prsht.h contains all of the identifiers used with property sheets.

A property sheet contains one or more overlapping child windows called pages, each containing control windows for setting a group of related properties. For example, a page can contain the controls for setting the font properties of an item, including the type style, point size, color, and so on. Each page has a tab that the user can select to bring the page to the foreground of the property sheet. For example, the Date-Time control panel application displays the following property sheet.

screen shot of a property sheet with two tabs, one of which shows a clock and a monthly calendar control

A standard property sheet with multiple, tabbed pages allows the user random access to all properties. If it is more appropriate to have properties set in sequence, you can use a wizard.

Property Sheet Dialog Boxes

A property sheet and the pages it contains are actually dialog boxes. The property sheet is a system-defined dialog box that manages the pages and provides a common container for them. A property sheet dialog box can be modal or modeless. It includes a frame, a title bar, and four buttons: OK, Cancel, Apply, and (optionally) Help. The dialog box procedures for the pages receive notification codes in the form of WM_NOTIFY messages when the user clicks the buttons.

Note

Not all the information in this section applies to wizards, which have a somewhat different appearance and behavior. For example, wizards have a different set of buttons and no tabs. For more information, see Creating Wizards.

Each page in a property sheet is an application-defined modeless dialog box that manages the control windows used to view and edit the properties of an item. You provide the dialog box template used to create each page as well as the dialog box procedure that manages the controls and sets the properties of the corresponding item.

A property sheet sends notification codes to the dialog box procedure for a page when the page is gaining or losing the activation and when the user clicks the OK, Cancel, Apply, or Help button. The notifications are sent in the form of WM_NOTIFY messages. The lParam parameter is the address of an NMHDR structure that includes the window handle to the property sheet dialog box.

Some notification codes require a page to return either TRUE or FALSE in response to the WM_NOTIFY message. To do this, the page must use the SetWindowLong function to set the DWL_MSGRESULT value for the page dialog box to either TRUE or FALSE.

Pages

A property sheet must contain at least one page, but it cannot contain more than the value of MAXPROPPAGES as defined in the Windows header files. Each page has a zero-based index that the property sheet assigns according to the order in which the page is added to the property sheet. The indexes are used in messages that you send to the property sheet.

A property page can contain a nested dialog box. If it does, you must include the WS_EX_CONTROLPARENT style for the top-level dialog box and call the IsDialogMessage function with the handle to the parent dialog box. This ensures that the user can use mnemonics and the dialog box navigation keys to move the focus to controls in the nested dialog box.

Each page has a corresponding icon and label. The property sheet creates a tab for each page and displays the icon and label in the tab. All property sheet pages are expected to use a nonbold font. To ensure that the font is not bold, specify the DS_3DLOOK style in the dialog box template.

The dialog box procedure for a page must not call the EndDialog function. Doing so will destroy the entire property sheet, not just the page.

The minimum size for a property sheet page is 212 dialog units horizontally and 114 dialog units vertically. If a page dialog is smaller than this, the page will be enlarged until it meets the minimum size. The Prsht.h header file contains three sets of recommended sizes for property sheet pages, as shown in the following table.

Size Description
PROP_SM_CXDLG Width, in dialog units, of a small property sheet page.
PROP_SM_CYDLG Height, in dialog units, of a small property sheet page.
PROP_MED_CXDLG Width, in dialog units, of a medium-sized property sheet page.
PROP_MED_CYDLG Height, in dialog units, of a medium-sized property sheet page.
PROP_LG_CXDLG Width, in dialog units, of a large property sheet page.
PROP_LG_CYDLG Height, in dialog units, of a large property sheet page.

Using these recommended sizes will help ensure visual consistency between your application and other Microsoft Windows applications.

In the Microsoft Visual Studio resource editor, you can create a page of the appropriate size in the Add Resource dialog box. Expand the Dialog node and select IDD_PROPPAGE_LARGE, IDD_PROPPAGE_MEDIUM, or IDD_PROPPAGE_SMALL.

The property sheet is automatically sized to accommodate the largest page.

Property Sheet Creation

Before creating a property sheet, you must define one or more pages. This involves filling a PROPSHEETPAGE structure with information about the page—its icon, label, dialog box template, dialog box procedure, and so on—and then specifying the address of the structure in a call to the CreatePropertySheetPage function. The function returns a handle to the HPROPSHEETPAGE type that uniquely identifies the page.

To create a property sheet, you specify the address of a PROPSHEETHEADER structure in a call to the PropertySheet function. The structure defines the icon and title for the property sheet and also includes the address of an array of HPROPSHEETPAGE handles that you obtain by using CreatePropertySheetPage. When PropertySheet creates the property sheet, it includes the pages identified in the array. The pages appear in the property sheet in the same order that they are contained in the array.

Another way to assign pages to a property sheet is to specify an array of PROPSHEETPAGE structures instead of an array of HPROPSHEETPAGE handles. In this case, PropertySheet creates handles for the pages before adding them to the property sheet.

When a page is created, its dialog box procedure receives a WM_INITDIALOG message. The message's lParam parameter is a pointer to a copy of the PROPSHEETPAGE structure that is defined when the page is created. In particular, when a page is created, the structure's lParam member can be used to pass application-defined information to the dialog box procedure. With the exception of the lParam member, this structure must be treated as read-only. Modifying anything other than lParam will have unpredictable consequences.

When the system subsequently passes a copy of the page's PROPSHEETPAGE structure to your application, it uses the same pointer. Any changes to the structure will be passed along. Because the lParam member is ignored by the system, it can be modified to send information to other parts of your application. You can, for instance, use lParam to pass information to the page's PropSheetPageProc callback function.

PropertySheet automatically sets the size and initial position of a property sheet. The position is based on the position of the owner window, and the size is based on the largest page specified in the array of pages when the property sheet was created. If you want the pages to match the width of the four buttons at the bottom of the property sheet, set the width of the widest page to 190 dialog units.

The size of a property sheet is computed from the width and height properties of the dialog template in the resource file. See DIALOG Resource or DIALOGEX Resource for further details. Note, however, that for compatibility reasons, the dimensions are computed relative to the MS Shell Dlg font rather than the font used by the page. If you design a page that uses another font, one of the following suggestions can be used.

  • Adjust the dimensions of the dialog template to compensate for the difference in size between the MS Shell Dlg font and the font the page actually uses. For example, if you choose a font that is twice as wide as MS Shell Dlg, then set the dialog template's width property to twice the normal use.
  • Use a DIALOGEX template and set the DS_SHELLFONT dialog style. In that case, the property sheet manager interprets the dialog template dimensions relative to the font used by the dialog template.

Adding and Removing Pages

After creating a property sheet, an application can add a page to the end of the existing set of pages by sending a PSM_ADDPAGE message. To insert a page between existing pages, send a PropSheet_InsertPage message. Note that the size of the property sheet cannot change after it has been created. Any added or inserted pages must be no larger than the largest page currently in the property sheet. To remove a page, send a PSM_REMOVEPAGE message.

When you define a page, you can specify the address of a PropSheetPageProc callback function that the property sheet calls when it is creating or removing the page. Using PropSheetPageProc gives you an opportunity to perform initialization and cleanup operations for individual pages.

Note

A number of messages and one function call occur while the property sheet is manipulating the list of pages. While this action is taking place, attempting to modify the list of pages will have unpredictable results. Do not add, insert, or remove pages in your implementation of PropSheetPageProc, or while handling the following notifications and Windows messages.

If the need arises to modify a property sheet page while you are handling one of these messages or while PropSheetPageProc is in operation, post a private Windows message. Your application will not receive that message until after the property sheet manager has finished its tasks, at which point it will be safe to modify the list of pages.

When a property sheet is destroyed, it automatically destroys all of the pages that have been added to it. The pages are destroyed in reverse order from that specified in the array used to create the pages. To destroy a page that was created by the CreatePropertySheetPage function but was not added to the property sheet, use the DestroyPropertySheetPage function.

Property Sheet Title and Page Labels

You specify the title of a property sheet in the PROPSHEETHEADER structure used to create the property sheet. If the dwFlags member includes the PSH_PROPTITLE value, the property sheet adds the suffix "Properties" or the prefix "Properties for", depending on the version. You can change the title after a property sheet is created by using the PSM_SETTITLE message. In an Aero Wizard, this message can be used to change the title of an interior page dynamically.

By default, a property sheet uses the name string specified in the dialog box template as the label for a page. You can override the name string by including the PSP_USETITLE value in the dwFlags member of the PROPSHEETPAGE structure that defines the page. When PSP_USETITLE is specified, the pszTitle member must contain the address of the label string for the page.

Page Activation

A property sheet can have only one active page at a time. The page that has the activation is at the foreground of the overlapping stack of pages. The user activates a page by selecting its tab; an application activates a page by using the PSM_SETCURSEL message.

The property sheet sends the PSN_KILLACTIVE notification code to the page that is about to lose the activation. In response, the page must validate any changes that the user has made to the page. If the page requires additional user input before losing the activation, use the SetWindowLong function to set the DWL_MSGRESULT value of the page to TRUE. Also, the page must display a message box that describes the problem and provides the recommended action. Set DWL_MSGRESULT to FALSE when it is okay to lose the activation.

Before the page that is gaining the activation is visible, the property sheet sends the PSN_SETACTIVE notification code to the page. The page must respond by initializing its control windows.

Help Button

Property sheets can display two Help buttons: a property sheet Help button that is displayed at the bottom of the frame, next to the OK/Cancel/Apply buttons, and a standard caption bar button that provides context-sensitive Help.

The property sheet Help button is optional, and can be enabled on a page by page basis. To display the property sheet Help button for one or more pages:

  • Set the PSH_HASHELP flag in the dwFlags member of the property sheet's PROPSHEETHEADER structure.
  • For each page that will display a Help button, set the PSP_HASHELP flag in the dwFlags member of the page's PROPSHEETPAGE structure.

When the user clicks the Help button, the active page receives a PSN_HELP notification code. The page must respond by displaying Help information, typically by calling the WinHelp function.

Removing the Caption Bar Help Button

The caption bar Help button is displayed by default, so that context-sensitive Help is always available for the OK/Cancel/Apply buttons. However, this button can be removed, if necessary. To remove a property sheet's caption bar Help button:

  • For versions of the common controls prior to version 5.80, you must implement a property sheet callback function.
  • For version 5.80 and later of the common controls, you can simply set the PSH_NOCONTEXTHELP flag in the dwFlags member of the property sheet's PROPSHEETHEADER structure. However, if you need backward compatibility with earlier common control versions, you must implement the callback function.

To implement a property sheet callback function that removes the caption bar Help button:

  • Set the PSH_USECALLBACK flag in the dwFlags member of the property sheet's PROPSHEETHEADER structure.
  • Set the pfnCallBack member of the PROPSHEETHEADER structure to point to the callback function.
  • Implement the callback function. When this function receives the PSCB_PRECREATE message, it will also receive a pointer to the property sheet's dialog box template. Remove the DS_CONTEXTHELP style from this template.

The following sample illustrates how to implement such a callback function:

int CALLBACK RemoveContextHelpProc(HWND hwnd, UINT message, LPARAM lParam)
{
    switch (message) 
    {
    case PSCB_PRECREATE:
        // Remove the DS_CONTEXTHELP style from the
        // dialog box template
        if (((LPDLGTEMPLATEEX)lParam)->signature ==    
           0xFFFF)
           {
            ((LPDLGTEMPLATEEX)lParam)->style 
            &= ~DS_CONTEXTHELP;
        }
        else {
            ((LPDLGTEMPLATE)lParam)->style 
            &= ~DS_CONTEXTHELP;
        }
        return TRUE;
    }
    return TRUE;
}

If the DLGTEMPLATEEX structure is not defined, include the following declaration:

#include <pshpack1.h>

typedef struct DLGTEMPLATEEX
{
    WORD dlgVer;
    WORD signature;
    DWORD helpID;
    DWORD exStyle;
    DWORD style;
    WORD cDlgItems;
    short x;
    short y;
    short cx;
    short cy;
} DLGTEMPLATEEX, *LPDLGTEMPLATEEX;

#include <poppack.h>

OK, Cancel, and Apply Buttons

The OK and Apply buttons are similar; both direct a property sheet's pages to validate and apply the property changes that the user has made. The only difference is that clicking the OK button causes the property sheet to be destroyed after the changes are applied.

When the user clicks the OK or Apply button, the property sheet sends a PSN_KILLACTIVE notification to the active page, giving it an opportunity to validate the user's changes. If the changes are valid, the page must call the SetWindowLong function with the DWL_MSGRESULT value set to FALSE. If the user's changes are not valid, the page must set DWL_MSGRESULT to TRUE and display a dialog box informing the user of the problem. The page remains active until it sets DWL_MSGRESULT to FALSE in response to a PSN_KILLACTIVE message.

After a page responds to a PSN_KILLACTIVE notification by setting DWL_MSGRESULT to FALSE, the property sheet will send a PSN_APPLY notification to each page. When a page receives this notification, it must apply the new properties to the corresponding item. To indicate to the property sheet that the changes are valid for the page, call SetWindowLong with DWL_MSGRESULT set to PSNRET_NOERROR. If the changes are invalid for the page, return an error. Doing so prevents the property sheet from being destroyed and returns focus to either the page that received the PSN_APPLY notification or the page that had focus when the Apply button was pressed. To return an error, and indicate which page will receive focus, set DWL_MSGRESULT to one of the following values.

  • PSNRET_INVALID. The property sheet will not be destroyed, and focus will be returned to this page.
  • PSNRET_INVALID_NOCHANGEPAGE. The property sheet will not be destroyed, and focus will be returned to the page that had focus when the button was pressed.

An application can use the PSM_APPLY message to simulate the selection of the Apply button.

The Apply button is initially disabled when a page becomes active, indicating that there are not yet any property changes to apply. When the page receives input through one of its controls indicating that the user has edited a property, the page must send the PSM_CHANGED message to the property sheet. The message causes the property sheet to enable the Apply button. If the user subsequently clicks the Apply or Cancel button, the page must reinitialize its controls and then send the PSM_UNCHANGED message to again disable the Apply button.

Sometimes the Apply button causes a page to make a change to a property sheet, and the change cannot be undone. When this happens, the page must send the PSM_CANCELTOCLOSE message to the property sheet. The message causes the property sheet to change the text of the OK button to "Close," indicating that the applied changes cannot be canceled.

Sometimes a page makes a change to the system configuration that requires Windows to be restarted or the system rebooted before the change can take effect. After making such a change, a page must send either the PSM_RESTARTWINDOWS or PSM_REBOOTSYSTEM message to the property sheet. These messages cause the PropertySheet function to return the ID_PSRESTARTWINDOWS or ID_PSREBOOTSYSTEM value after the property sheet is destroyed.

When a user clicks the Cancel button, the property sheet sends the PSN_RESET notification code to all pages, indicating that the property sheet is about to be destroyed. A page must use the notification to perform cleanup operations.

Wizards

A wizard is a special type of property sheet. Wizards are designed to present pages one at a time in a sequence that is controlled by the application. Instead of selecting from a group of pages by clicking a tab, users navigate forward and backward through the sequence, one page at a time, by clicking buttons. For example, the following screen shot shows the welcome page from the Add Hardware wizard:

screen shot of the welcome page of a wizard

The following screen shot shows the first page of an Aero Wizard, the new style introduced in Windows Vista.

screen shot of the first page of an aero wizard

See Creating Wizards for a complete discussion of wizards.