Home

Wizard Pages

 

Introdution

 

A wizard is a series of dialog boxes that are accessed consecutively. A wizard can consist of two or more pages. When it starts, it presents the starting or first page and it usually displays two or more buttons. One of the buttons may be disabled and display a caption as Back. Here is an example from Microsoft Access:

Wizard Pages

To exploit it, the user would click a Next or Back buttons. The Next button allows the user to access the page that succeeds the current one. The Back button implements the opposite, allowing the user to access the previous page.

When on the first page, after making a selection, the user can click Next. This would enable the Back button. The wizard should have another button labeled Next. After making a selection on a page, the user can click Next. Most of the time, a wizard also has a button labeled Finish:

Wizard Pages

In this case, at any time, the user can click the Finish. In some cases, a wizard has only two buttons with captions as Back and Next. When in the first page, the left button is disabled. When the user gets to the last button, the right button changes from Next to Finish.

By default, a wizard has at least two buttons labeled Back and Next. As mentioned already, when the user gets to the last page, the Next button becomes Finish. In some cases, such as described for the above wizard in Microsoft Access, the Finish button is separate. When the user gets to the last page, the Next button becomes disabled:

Wizard Pages

A wizard can also be equipped with a Cancel button. If the wizard is equipped with a Cancel button, the user can close and dismiss it at any time. This, by default allows you to ignore anything the user did on the wizard. Another wizard may have a Help button.

All this descrption is left up to you to decide what buttons to display on your wizard, what to do with the buttons, why, and when. All options are possible and usually easy to configure.

Practical LearningPractical Learning: Introducing Wizards

  1. To create a new application, on the main menu, click File -> New Project...
  2. In the left list, click Visual C++. In the right list, click MFC Application
  3. Change the Name to AutoRepair1
  4. Click OK
  5. In the first page of the wizard, click Next
  6. In the second page of the wizard, click Dialog Based
     
    MFC Application Wizard
  7. Click Next
  8. In the third page, click About Box to remove its check mark and click Next
  9. In the fourth page of the wizard, click Next
  10. In the fifth page of the wizard and in the General Classes list, click CAutoRepairDlg
  11. In the Class Name, change the name of the class to CCustomerInformationDlg
  12. Change the .h File to CustomerInformationDlg.h
  13. Change the .cpp File to CustomerInformationDlg.cpp
     
    MFC Application Wizard
  14. Click Finish
  15. On the dialog box, click the TODO line and press Delete
  16. While the OK button is selected, press Delete
  17. Also delete the Cancel button
  18. In the Toolbox, click Button and click the body of the dialog box

Creating a Wizard

As seen with the property sheet, there are a few steps you must follow to create a wizard. The steps are primarily the same as those of a property sheet. Start by creating a dialog box for each wizard page. When designing a page, set the size you want. If your wizard will have more than one page (it is possible but not practical to have a wizard with only one page), the width of the largest dialog box will be applied to the other page(s). The height of the tallest dialog box will be used for all pages.

After designing a page, create a class for it. For the base class, select CPropertyPage. After creating the pages and their classes, derive a class from CPropertySheet. In a constructor of the CPropertySheet-based class, add each page.

Practical LearningPractical Learning: Creating Wizard Pages

  1. In the main window, click the body of the dialog box
  2. In the Properties window, change the ID to IDD_CUSTOMERINFO_DLG
  3.  In the Solution Explorer, expand the project and the Header Files node. Double-click CustomerInformationDlg.h
  4. Change the document as follows:
    // CustomerInformationDlg.h : header file
    //
    #pragma once
    
    // CCustomerInformationDlg dialog
    class CCustomerInformationDlg : public CPropertyPage
    {
    // Construction
    public:
    	CCustomerInformationDlg(CWnd* pParent = NULL); // standard constructor
    
    // Dialog Data
    	enum { IDD = IDD_CUSTOMERINFO_DLG };
    
    	protected:
    	virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
    
    // Implementation
    protected:
    	HICON m_hIcon;
    
    	// Generated message map functions
    	virtual BOOL OnInitDialog();
    	afx_msg void OnPaint();
    	afx_msg HCURSOR OnQueryDragIcon();
    	DECLARE_MESSAGE_MAP()
    };
  5. Access the CustomerInformationDlg.cpp file and change it as follows:
    // CustomerInformationDlg.cpp : implementation file
    //
    
    #include "stdafx.h"
    #include "AutoRepair1.h"
    #include "CustomerInformationDlg.h"
    #include "afxdialogex.h"
    
    #ifdef _DEBUG
    #define new DEBUG_NEW
    #endif
    
    // CCustomerInformationDlg dialog
    
    CCustomerInformationDlg::CCustomerInformationDlg(CWnd* pParent /*=NULL*/)
    	: CPropertyPage(CCustomerInformationDlg::IDD)
    {
    	m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
    }
    
    void CCustomerInformationDlg::DoDataExchange(CDataExchange* pDX)
    {
    	CPropertyPage::DoDataExchange(pDX);
    }
    
    BEGIN_MESSAGE_MAP(CCustomerInformationDlg, CPropertyPage)
    	ON_WM_PAINT()
    	ON_WM_QUERYDRAGICON()
    END_MESSAGE_MAP()
    
    
    // CCustomerInformationDlg message handlers
    
    BOOL CCustomerInformationDlg::OnInitDialog()
    {
    	CPropertyPage::OnInitDialog();
    
    	// Set the icon for this dialog.  The framework does this automatically
    	//  when the application's main window is not a dialog
    	SetIcon(m_hIcon, TRUE);			// Set big icon
    	SetIcon(m_hIcon, FALSE);		// Set small icon
    
    	// TODO: Add extra initialization here
    
    	return TRUE;  // return TRUE  unless you set the focus to a control
    }
    
    // If you add a minimize button to your dialog, you will need the code below
    //  to draw the icon.  For MFC applications using the document/view model,
    //  this is automatically done for you by the framework.
    
    void CCustomerInformationDlg::OnPaint()
    {
        if (IsIconic())
        {
    	CPaintDC dc(this); // device context for painting
    
    	SendMessage(WM_ICONERASEBKGND, 
    		    reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);
    
    	// Center icon in client rectangle
    	int cxIcon = GetSystemMetrics(SM_CXICON);
    	int cyIcon = GetSystemMetrics(SM_CYICON);
    	CRect rect;
    	GetClientRect(&rect);
    	int x = (rect.Width() - cxIcon + 1) / 2;
    	int y = (rect.Height() - cyIcon + 1) / 2;
    
    		// Draw the icon
    		dc.DrawIcon(x, y, m_hIcon);
    	}
    	else
    	{
    		CPropertyPage::OnPaint();
    	}
    }
    
    HCURSOR CCustomerInformationDlg::OnQueryDragIcon()
    {
    	return static_cast<HCURSOR>(m_hIcon);
    }
  6. Display the dialog box of the project
  7. Click the OK button and press Delete
  8. Click the Cancel button and press Delete
  9. In the Properties window, change the following characteristics of the dialog box:
    Border: Thin
    Caption: Customer Information
    Disabled: True
    Style: Child
  10. If the Resource View is not visible, on the main menu, click View -> Other Windows -> Resource View.
    In the Resource View, expand the name of the project. Expand AutoRepair1.rc. Expand Dialog.
    Right-click Dialog and click Insert Dialog
  11. On the new dialog box, click OK and press Delete
  12. As the Cancel button is selected, press Delete
  13. In the Properties window, change the following characteristics of the dialog box:
    Border: Thin
    Caption: Job Performed/Parts Used
    Disabled: True
    ID: IDD_JOBSANDPARTS_DLG
    Style: Child
  14. In the Toolbox, click Combo Box and click the body of the dialog box
  15. To create a class for the dialog box, right-click the body of the dialog box and click Add Class...
  16. Set the Class Name to CJobsAndPartsDlg
  17. Set the Base Class to CPropertyPage
     
    MFC Add Class Wizard
  18. Click Finish
  19. To create a new property page, in the Resource View, right-click Dialog and click Add Resource...
  20. In the Add Resource dialog box, expand Dialog and click IDD_PROPPAGE_LARGE
  21. Click New
  22. In the Properties window, change the following characteristics:
    Caption: Repair Summary
    ID: IDD_REPAIRSUMMARY_DLG
  23. In the Toolbox, click Month Calendar Control and click the body of the dialog box
  24. Right-click the body of the dialog box and click Add Class...
  25. Set the Class Name to CRepairSummaryDlg
  26. Set the Base Class to CPropertyPage
  27. Click Finish
  28. To create a new class, on the main menu, click Project -> Add Class...
  29. Click MFC Class and click Add
  30. Set the Class Name to CRepairOrderSheet
  31. Set the Base Class to CPropertySheet
     
    MFC Add Class Wizard
  32. Click Finish
  33. Change the RepairOrderSheet.h document as follows:
    #pragma once
    
    #include "CustomerInformationDlg.h"
    #include "JobsAndPartsDlg.h"
    #include "RepairSummaryDlg.h"
    
    // CRepairOrderSheet
    
    class CRepairOrderSheet : public CPropertySheet
    {
    	DECLARE_DYNAMIC(CRepairOrderSheet)
    
    public:
    	CRepairOrderSheet(UINT nIDCaption, 
    		 	  CWnd* pParentWnd = NULL,
    			  UINT iSelectPage = 0);
    	CRepairOrderSheet(LPCTSTR pszCaption,
    			  CWnd* pParentWnd = NULL,
    			  UINT iSelectPage = 0);
    	virtual ~CRepairOrderSheet();
    
    private:
    	CCustomerInformationDlg dlgCustomerInformation;
    	CJobsAndPartsDlg dlgJobsAndParts;
    	CRepairSummaryDlg dlgRepairSummary;
    
    protected:
    	DECLARE_MESSAGE_MAP()
    };
  34. Access the RepairOrderSheet.dpp file and change the constructor that takes a string as its first argument as follows:
    CRepairOrderSheet::CRepairOrderSheet(LPCTSTR pszCaption,
    		  		     CWnd* pParentWnd, UINT iSelectPage)
    	:CPropertySheet(pszCaption, pParentWnd, iSelectPage)
    {
    	this->AddPage(&dlgCustomerInformation);
    	this->AddPage(&dlgJobsAndParts);
    	this->AddPage(&dlgRepairSummary);
    }
 
 
 

Setting the Wizard Mode

The big difference between a property sheet and a wizard is that, before showing the dialog box, you must call the SetWizardMode() member function of the CPropertySheet class. Its syntax is:

void SetWizardMode();

As you can see, this member function takes no argument. It simply informs the compiler that, instead of a property sheet, you want to create a wizard. The real question is, where do you call this member function? That's because you have various options. The secret is to call before displaying the pages; that is, before calling CDialog::DoModal(). You can call CPropertySheet::SetWizardMode() in the same constructore where you add the pages, or you can call it just before calling CDialog::DoModal().

Practical LearningPractical Learning: Calling a Wizard

  1. In the Solution Explorer, double-click AutoRepair1.cpp and change it as follows:
    // AutoRepair1.cpp : Defines the class behaviors for the application.
    //
    
    #include "stdafx.h"
    #include "AutoRepair1.h"
    #include "CustomerInformationDlg.h"
    #include "RepairOrderSheet.h"
    
    #ifdef _DEBUG
    #define new DEBUG_NEW
    #endif
    
    
    // CAutoRepair1App
    
    BEGIN_MESSAGE_MAP(CAutoRepair1App, CWinApp)
    	ON_COMMAND(ID_HELP, &CWinApp::OnHelp)
    END_MESSAGE_MAP()
    
    
    // CAutoRepair1App construction
    
    CAutoRepair1App::CAutoRepair1App()
    {
    	// support Restart Manager
    	m_dwRestartManagerSupportFlags = AFX_RESTART_MANAGER_SUPPORT_RESTART;
    
    	// TODO: add construction code here,
    	// Place all significant initialization in InitInstance
    }
    
    
    // The one and only CAutoRepair1App object
    
    CAutoRepair1App theApp;
    
    
    // CAutoRepair1App initialization
    
    BOOL CAutoRepair1App::InitInstance()
    {
    	// InitCommonControlsEx() is required on Windows XP if an application
    	// manifest specifies use of ComCtl32.dll version 6 or later to enable
    	// visual styles.  Otherwise, any window creation will fail.
    	INITCOMMONCONTROLSEX InitCtrls;
    	InitCtrls.dwSize = sizeof(InitCtrls);
    	// Set this to include all the common control classes you want to use
    	// in your application.
    	InitCtrls.dwICC = ICC_WIN95_CLASSES;
    	InitCommonControlsEx(&InitCtrls);
    
    	CWinApp::InitInstance();
    
    	AfxEnableControlContainer();
    
    	// Create the shell manager, in case the dialog contains
    	// any shell tree view or shell list view controls.
    	CShellManager *pShellManager = new CShellManager;
    
    	// Standard initialization
    	// If you are not using these features and wish to reduce the size
    	// of your final executable, you should remove from the following
    	// the specific initialization routines you do not need
    	// Change the registry key under which our settings are stored
    	// TODO: You should modify this string to be something appropriate
    	// such as the name of your company or organization
    	SetRegistryKey(_T("Local AppWizard-Generated Applications"));
    
    	// CCustomerInformationDlg dlg;
    	CRepairOrderSheet dlg(L"College Park Auto Repair");
    	m_pMainWnd = &dlg;
    	dlg.SetWizardMode();
    
    	INT_PTR nResponse = dlg.DoModal();
    	if (nResponse == IDOK)
    	{
    		// TODO: Place code here to handle when the dialog is
    		//  dismissed with OK
    	}
    	else if (nResponse == IDCANCEL)
    	{
    		// TODO: Place code here to handle when the dialog is
    		//  dismissed with Cancel
    	}
    
    	// Delete the shell manager created above.
    	if (pShellManager != NULL)
    	{
    		delete pShellManager;
    	}
    
    	// Since the dialog has been closed, return FALSE so that we exit the
    	//  application, rather than start the application's message pump.
    	return FALSE;
    }
  2. To execute the project, press F5
     
    Wizard
  3. Notice that the Back button is enabled.
    Click Next
     
    Wizard
  4. Click Next again
     
    Wizard
  5. Notice that there is no Finish button although you are on the last page.
    Close the dialog box and return to your programming environment

Configuring the Default Buttons of a Wizard

By default, after creating a regular wizard, it is equipped with three buttons: Back, Next, and Cancel. If you select the option to add help in the MFC Application, the wizard would also be equipped with a Help button. You can manage the buttons and you have many options.

As with anything else in the world or programming, you should make your wizard intuitive and friendly. For example, on the first page, since there is no reason for the user to access the previous page, you can disable the Back button. On the other hand, when the user gets to the last page, since there is no succeeding page, there is no need for a Next button. Consequently, you should replace it with a Finish button. Fortunately, the display of these buttons is probably the easiest thing to manage on a wizard.

In order to decide what button(s) should be available when a particular page is accessed, generate its CPropertyPage::OnSetActive() event. Its syntax is:

virtual BOOL OnSetActive( );

This event fires when a page of your choice is accessed, allowing you to do what you want on the wizard page. One of the actions you can take when a page displays is to decide what button(s) should be displayed on the wizard. The buttons of a wizard are managed through the CPropertySheet::SetWizardButtons() member function. Its syntax is:

void SetWizardButtons(DWORD dwFlags);

The dwFlags argument is a constant or a combination of values that defines the buttons to display. The possible values are:

  • PSWIZB_BACK for the Back button
  • PSWIZB_NEXT for the Next button
  • PSWIZB_FINISH for the Finish button
  • PSWIZB_DISABLEDFINISH to disable the Finish button

On the first page, to display the Next button while disabling the Back button, you can pass only the PSWIZB_NEXT value. On the last page, you should display the Back and the Finish buttons. All pages between the first and the last should display the Back and the Next buttons, unless you have a reason to do otherwise.

Adding the Default Buttons of a Wizard

To add your own button to the wizard, you can use the features of the CWnd class. Here is an example:

CmyWizardSheet::OnInitDialog()
{
    BOOL bResult = CPropertySheet::OnInitDialog();

    CButton *btnWhatever = new CButton;

    btnWhatever->Create(L"Whatever", WS_CHILD | WS_VISIBLE,
		        CRect(10, 362, 126, 385), this, 0x122);

    Return bResult;
}

When the user clicks the Back button, the CPropertyPage::OnWizardBack() event fires, giving you the opportunity to do what you judge necessary. Its syntax is:

virtual LRESULT OnWizardBack();

You should use this event on a page that has the Back button and if this button is enabled. When the user clicks the Next button, the CPropertyPage::OnWizardNext() event fires, allowing you to take appropriate measures. Its syntax is:

virtual LRESULT OnWizardNext();

You should use this event on a page that has the Next button. When the user clicks the Finish button, the CPropertyPage::OnWizardFinish() event fires. Its syntax is:

virtual BOOL OnWizardFinish( );

This event should be implement when either the user is on the last page that has the Finish button or at any time if the wizard has a permanent Finish button available on all pages.

Practical LearningPractical Learning: Implementing Wizard Buttons

  1. On the main menu, click Project -> Class Wizard...
  2. In the Class Name combo box, make sure that CCustomerInformationDlg is selected (or select it).
    Click the Virtual Funtions tab
  3. In the Virtual Functions list, double-click OnSetActive
     
    MFC Class Wizard
  4. Click Edit Code
  5. Change the file as follows:
    // CustomerInformationDlg.cpp : implementation file
    //
    
    #include "stdafx.h"
    #include "AutoRepair1.h"
    #include "CustomerInformationDlg.h"
    #include "afxdialogex.h"
    #include "RepairOrderSheet.h"
    
    . . . No Change
    
    BOOL CCustomerInformationDlg::OnSetActive()
    {
        // TODO: Add your specialized code here and/or call the base class
        CRepairOrderSheet *ros = reinterpret_cast<CRepairOrderSheet *>(GetParent());
    
        ros->SetWizardButtons(PSWIZB_NEXT);
    
        return CPropertyPage::OnSetActive();
    }
  6. Press F5 to execute
     
    Wizard
  7. Notice that the Back button is now disabled.
    Close the dialog box and return to your programming environment
  8. On the main menu, click Project -> Class Wizard...
  9. In the Class Name combo box, select CRepairSummaryDlg
  10. Click the Virtual Funtions tab
  11. In the Virtual Functions list, double-click OnSetActive
  12. In the Class Name combo box, select CJobsAndPartsDlg
  13. In the Virtual Functions list, double-click OnSetActive
  14. Click Edit Code
  15. Change the file as follows:
    // JobsAndPartsDlg.cpp : implementation file
    //
    
    #include "stdafx.h"
    #include "AutoRepair1.h"
    #include "JobsAndPartsDlg.h"
    #include "afxdialogex.h"
    #include "RepairOrderSheet.h"
    
    . . . No Change
    
    BOOL CJobsAndPartsDlg::OnSetActive()
    {
        // TODO: Add your specialized code here and/or call the base class
        CRepairOrderSheet *ros = reinterpret_cast<CRepairOrderSheet *>(GetParent());
    
        ros->SetWizardButtons(PSWIZB_BACK | PSWIZB_NEXT);
    
        return CPropertyPage::OnSetActive();
    }
  16. Access the RepairSummaryDlg.cpp file
  17. Change the file as follows:
    // RepairSummaryDlg.cpp : implementation file
    //
    
    #include "stdafx.h"
    #include "AutoRepair1.h"
    #include "RepairSummaryDlg.h"
    #include "afxdialogex.h"
    #include "RepairOrderSheet.h"
    
    . . . No Change
    
    BOOL CRepairSummaryDlg::OnSetActive()
    {
        // TODO: Add your specialized code here and/or call the base class
        CRepairOrderSheet *ros = reinterpret_cast<CRepairOrderSheet *>(GetParent());
    
        ros->SetWizardButtons(PSWIZB_BACK | PSWIZB_FINISH);
    
        return CPropertyPage::OnSetActive();
    }
  18. To test the application, press F9
     
    Wizard
  19. Close the dialog box and return to your programming environment
  20. Access the RepairSummaryDlg.cpp file and delete | PSWIZB_FINISH from the code you wrote:
    OOL CRepairSummaryDlg::OnSetActive()
    {
    	// TODO: Add your specialized code here and/or call the base class
    	CRepairOrderSheet *ros = reinterpret_cast<CRepairOrderSheet *>(GetParent());
    
    	ros->SetWizardButtons(PSWIZB_BACK);
    
    	return CPropertyPage::OnSetActive();
    }
  21. On the main menu, click Project -> Class Wizard...
  22. In the Class Name combo box, select CRepairOderSheet and click the Virtual Functions tab
  23. In the Virtual Functions list, double-click OnInitDialog
  24. Click Edit Code
  25. Change the file as follows:
    BOOL CRepairOrderSheet::OnInitDialog()
    {
    	BOOL bResult = CPropertySheet::OnInitDialog();
    
    	// TODO:  Add your specialized code here
    	CButton *btnFinish;
    
    	btnFinish = reinterpret_cast<CButton *>(GetDlgItem(IDCANCEL));
    	btnFinish->SetWindowText(L"Finish");
    
    	CButton *btnClose;
    
    	btnClose = reinterpret_cast<CButton *>(GetDlgItem(IDHELP));
    	btnClose->SetWindowText(L"Close");
    
    	return bResult;
    }
  26. On the main menu, click Project -> Class Wizard...
  27. In the Class Name combo box, make sure CRepairOrderSheet is selected and click the Commands tab.
    In the Object IDs list, double-click IDCANCEL
  28. Change the Member Function Name to OnFinishRepairOrder
  29. Click OK
  30. In the Object IDs list, double-click IDHELP
  31. Change the Member Function Name to OnCloseRepairOrder
  32. Click OK
  33. Click Edit Code
  34. Implement the events as follows:
    void CRepairOrderSheet::OnFinishRepairOrder()
    {
        // TODO: Add your command handler code here
        AfxMessageBox(L"The Finish button of the wizard was clicked.");
    }
    
    void CRepairOrderSheet::OnCloseRepairOrder()
    {
        // TODO: Add your command handler code here
        AfxMessageBox(L"The repair order will be closed!");
    	PostQuitMessage(0);
    }
  35. To execute, press F9
     
    Wizard
     
    Wizard
     
    Wizard
     
    Wizard
     
    Wizard
  36. Close the dialog box and return to your programming environment
 
 
   
 

Home Copyright © 2010 FunctionX, Inc.