I created the WIZARD sample in C to demonstrate how to manipulate wizard controls. (A version of this program also appears in the Win32 SDK.) The design goals for this sample were simple: first, to create a wizard in which you can step back and forth; and second, to make it fun. The second goal was the hardest. After a lot of thought, an inspiration finally came to me. So if you'll indulge me in a little company humor, let's get started.
Twice a year, each Microsoft employee participates in a performance review, evaluating our sterling accomplishments of the past six months, explaining how important we are to the success of the company, and listing all the reasons why we deserve a raise. Every time reviews roll around, we complain about having to do the paperwork, and every time I wonder why someone hasn't created a cool tool that would generate a performance review, given some basic data. So I decided to write a wizard to help with this sometimes painful exercise. In reality, of course, it ended up being used for nothing more than chuckles around here, but at least it made the sample a bit more fun.
The first step in creating any wizard is to create a dialog box for each page of information you want to collect. You can do this with the same resource editor you use for standard dialog boxes. For a wizard page, however, you should remove the OK and Cancel buttons that are included in the default dialog template.
After you've created the dialog boxes, you must write code that will first fill out a PROPSHEETPAGE structure for each page (dialog box) you plan to display and will then fill out a PROPSHEETHEADER structure for the overall property sheet. The dwFlags field of the PROPSHEETHEADER structure must include the PSH_WIZARD flag to specify that this particular property sheet is a wizard. Finally, the application must call the PropertySheet function. The following code demonstrates how to fill out these structures to create a wizard:
// FUNCTION: FillInPropertyPage (PROPSHEETPAGE *, int,
LPSTR, LPFN)
//
// PURPOSE: Fills out the given PROPSHEETPAGE structure
//
// COMMENTS:
// This function fills out a PROPSHEETPAGE structure with the
// information the system needs to create the page.
void FillInPropertyPage (PROPSHEETPAGE *psp, int idDlg, LPSTR
pszProc,
DLGPROC pfnDlgProc)
{
// Set the size of this structure.
psp->dwSize = sizeof (PROPSHEETPAGE);
// No special flags
psp->dwFlags = 0;
// The instance associated with this application
psp->hInstance = rvInfo.hInst;
// The dialog template to use
psp->pszTemplate = MAKEINTRESOURCE (idDlg);
// Don't use a special icon in the caption.
psp->pszIcon = NULL;
// The dialog procedure that handles this page
psp->pfnDlgProc = pfnDlgProc;
// The title for this page
psp->pszTitle = pszProc;
// No special application-specific data
psp->lParam = 0;
}
// FUNCTION: CreateWizard (HWND)
//
// PURPOSE: Creates the wizard control
//
// COMMENTS:
// This function creates the wizard property sheet.
int CreateWizard (HWND hwndOwner, HINSTANCE hInst)
{
PROPSHEETPAGE psp [NUM_PAGES];
PROPSHEETHEADER psh;
// For each page, fill out a PROPSHEETPAGE structure.
FillInPropertyPage (&psp[0], IDD_INFO,
"Your Information", YourInfo);
FillInPropertyPage (&psp[1], IDD_WORKHABITS,
"Work Habits", WorkHabits);
FillInPropertyPage (&psp[2], IDD_TEAMWORK,
"Team Work", TeamWork);
FillInPropertyPage (&psp[3], IDD_RELIABILITY,
"Reliability", Reliability);
FillInPropertyPage (&psp[4], IDD_GOALS,
"Attainment of Goals", Goals);
FillInPropertyPage (&psp[5], IDD_ADAPTATION,
"Adaptability to Change", Adaptation);
// Fill in the size of the PROPSHEETHEADER structure.
psh.dwSize = sizeof (PROPSHEETHEADER);
// Specify a wizard property sheet with no Apply button.
psh.dwFlags = PSH_PROPSHEETPAGE | PSH_WIZARD | PSH_NOAPPLYNOW;
// Specify the parent window.
psh.hwndParent = hwndOwner;
// The caption for the wizard
psh.pszCaption = (LPSTR)"Review Wizard";
// The number of pages in this wizard
psh.nPages = sizeof (psp) / sizeof (PROPSHEETPAGE);
// Point to the array of property sheet pages.
psh.ppsp = (LPCPROPSHEETPAGE) &psp;
// Create and run the wizard.
return PropertySheet (&psh);
}
Although the wizard control simplifies the task of creating a wizard, it doesn't perform magic: you still have to do a lot yourself. The preceding code simply fills out the structures and calls the function to create and run the wizard. If you want those dialog boxes to gather the data and use the information the user enters, you need to do some work in your dialog procedures.
Each dialog function, as specified by the pfnDlgProc member of the PROPSHEETPAGE structure, must process the messages and notifications it receives. Property sheets rely heavily on notifications, packaged as WM_NOTIFY messages. The code used to trap the wizard notifications is similar to the code used for standard property sheets. Three special notifications are associated with wizards, however:
PSN_WIZBACK | Sent to the property sheet page when the user clicks the Back button |
PSN_WIZNEXT | Sent to the property sheet page when the user clicks the Next button |
PSN_WIZFINISH | Sent to the property sheet page when the user clicks the Finish button |
When these notifications are sent, the default action is to advance to the next page or to move back to the previous page. The application's notification handler can disallow either action by setting the notification result to -1. But that's not all.
Let's say you want your wizard to branch to a specific page depending on certain user input. For example, your wizard installs a piece of software, and your application must prompt for extra information depending on whether the user prefers a standard setup or a custom setup. The default behavior is to display the next page in the array of property sheet pages. But you can override that behavior and branch to a specific page by setting the notification result to the ID of the page you needthink of it as a visual GOTO. Your application could by default display the pages for custom setup in order, but it could branch past those pages if the user wants a standard setup.