About Wizards
- Creating Wizards
- Implementing Wizards
- Advanced Wizards
About Wizards
A wizard is a very useful user interface concept. It allows an application to easily
guide a user through a complete task. The purpose of a wizard is to collect information on
a task in a structured way, while being an organised user interface, and not displaying
large numbers of dialogs.
A wizard achieves this by creating multiple page dialogs that the user steps through
using the navigation buttons at the bottom of the dialog.
Examples of wizards are the AppWizard in Visual C++ and the Install New Hardware wizard
in Windows.
Creating Wizards
In MFC, wizards are very easy to implement. MFC provides the CPropertySheet and
CPropertyPage classes for creating property sheets, and wizards are implemented in the
same fashion, but with some minor modifications.
The easiest way to implement wizards is to use the Insert Components and Controls
option on the Project menu of Visual C++, and select Property Sheet. This displays a
wizard, which guides you through the process of creating the wizard. In step 1, select the
Wizard options button. Click Next, and then Next again. This then displays a wizard page
with the current classes in your program. Select the class you want to call the wizard
from and click Next. This wizard page asks you how many pages you want in your wizard.
Select the number you want, from 1 to 9, and click Next. A list box is then displayed
with the names of the wizard and its property pages. Click Change to change the selected
one, and change the selected information, click OK, then Finish.
Close the insert component dialog box, and open the Resource View, and expand the
Dialog item. You should see that more dialogs have been created, depending on the number
of pages you wanted in your wizard. They are probably called IDD_PROPPAGE1 or something
like that. You can rename them, but you must also change their names in the wizard page
header (.h) file.
Implementing Wizards
To call the wizard from your program, open the code for the command to call it from and
use:
CWizard().DoModal();
Change CWizard to your wizard's main class name.
Compile and run your program. Select the command that calls your wizard. You should see
your wizard appear, but it will look very basic. You can add controls to the dialogs just
like other dialogs, and use them just like on dialogs.To control the navigation buttons at the bottom of the wizard pages, you have to use
the features provided by CPropertyPage, using these messages:OnSetActive is called when the page becomes active
OnKillActive is called when the page is no longer active
OnWizardNext is called when the wizard’s Next button is
pressedOnWizardBack is called when the wizard’s Back button is
pressedOnWizardFinish is called when the wizard’s Finish button
is pressedThe OnSetActive() message is the message that plays the main role in adding the
button functionality. Once a page is notified that it is active, it must then call SetWizardButtons()
in the parent CWizardDialog object to change the button states.Use ClassWizard to add a OnSetActive() handler to each CPage class in your
wizard.In the first page's OnSetActive() function, add this code:
CWizard* pParent = (CWizard*)GetParent();
ASSERT_KINDOF(CWizard, pParent);
pParent->SetWizardButtons(PSWIZB_NEXT);This code gets a pointer to the parent CWizard and calls SetWizardButtons() to
only enable the Next button, and disable the Back button as it is the first wizard page.Replace CWizard with your wizard’s main class name, and add an include to the
wizard header file.In the second page's OnSetActive() function, add this code:
CWizard* pParent = (CWizard*)GetParent();
ASSERT_KINDOF(CWizard, pParent);
pParent->SetWizardButtons(PSWIZB_BACK | PSWIZB_NEXT);This code gets a pointer to the parent CWizard and calls SetWizardButtons() to
enable both the Next button and the Back button.In the third page's OnSetActive() function, add this code:
CWizard* pParent = (CWizard*)GetParent();
ASSERT_KINDOF(CWizard, pParent);
pParent->SetWizardButtons(PSWIZB_BACK | PSWIZB_FINISH);This code gets a pointer to the parent CWizard and calls SetWizardButtons()
to enable the Next button, and to display the Finish button instead of the Next button as
it is the last page.The SetWizardButtons() function can have the following flags:
SetWizardButtons(PSWIZB_NEXT) Disables the Back button and
enables the Next button.SetWizardButtons(PSWIZB_BACK) Disables the Next button and
enables the Back button.SetWizardButtons(PSWIZB_FINISH) Disables the Back button, and
replaces the Next button with an enabled Finish buttonSetWizardButtons(PSWIZB_BACK | PSWIZB_NEXT) Enables both the
Back and Next ButtonsSetWizardButtons(PSWIZB_BACK | PSWIZB_FINISH) Enables both
the Back and Finish ButtonsSetWizardButtons(PSWIZB_BACK | PSWIZB_DISABLEDFINISH) Enables
the Back and displays a disabled Finish button instead of the Next button.The PSWIZB_NEXT and PSWIZB_FINISH flags cannot be used together, because
they both use the same button position. See Advanced Wizards to find out how to do this.To find out what button was pressed in the wizard when you display the wizard, use:
If( CWizardDialog().DoModal() == ID_WIZFINISH ) { // Your Code }
This code will process the code placed where // Your Code is if the Finish button is
pressed on your wizard.
The titles of the dialogs which make up your wizard will be used as the page titles
when the wizard is run.
For example, if the dialog of the wizard's first step has a title "Wizard Step
1", then your wizard's title for the first wizard page will also be "Wizard Step
1".
Advanced Wizards
I have often been asked how to display three navigation buttons, (Back, Next and
Finish) on a wizard as Microsoft has done in the AppWizard. CPropertySheet only allows two
of the buttons to be displayed at a time, but really they are all there, but one is hidden
under another. The only way I have found to overcome this problem is to move the buttons
on the OnInitDialog of the first wizard page using this code:
BOOL CWizardPage::OnInitDialog(void)
{CPropertyPage::OnInitDialog();
((CPropertySheet*) GetParent())->SetWizardButtons(PSWIZB_NEXT|PSWIZB_FINISH);
CWnd *buttonBack = GetParent()->GetDlgItem(ID_WIZBACK);
CWnd *buttonNext = GetParent()->GetDlgItem(ID_WIZNEXT);// Now we have to move the buttons around
CRect BackButton;
buttonBack->GetWindowRect(&BackButton);
ScreenToClient(&BackButton);
buttonBack->SetWindowPos (NULL, BackButton.left - BackButton.Width() - 5, BackButton.top, BackButton.Width(), BackButton.Height(),
SWP_NOACTIVATE || SWP_NOSIZE || SWP_NOZORDER || SWP_SHOWWINDOW);buttonNext->SetWindowPos(buttonBack, BackButton.left, BackButton.top,
BackButton.Width(), BackButton.Height(), SWP_NOACTIVATE || SWP_SHOWWINDOW);buttonBack->EnableWindow(TRUE);
buttonBack->ShowWindow(SW_SHOW);buttonNext->EnableWindow(TRUE);
buttonNext->ShowWindow(SW_SHOW);return(TRUE);
}
Date Posted: 05/09/98