Introduction

There are two main differences between the traditional MFC-CPropertySheet wizards and the Windows 2000 wizards:

The Windows 2000 wizard has a white background.

The "page" portion of the Windows 2000 wizard extends all the way to the edge of the dialog.

The Microsoft GUI designers are fickle. I remember developing for Windows 3.1 and going through a lot of trouble to make grey dialog backgrounds instead of white backgrounds. Now we go through a lot of trouble to make white backgrounds instead of grey ones.

This article presents two classes that allows you to create Windows 2000 style wizards relatively easily. Both classes are actually derived from CDialog, rather than CPropertySheet or CPropertyPage. The classes make use of some techniques presented in Zoran M.Todorovic's CodeProject article on
stacked dialog boxes, however there are extensive code changes from Zoran's work.

Another big advantage of this method of creating wizards is that you have total control over the placement and text of the wizard buttons, and you can even add other control outside of the wizard pages.

The Classes

The CNewWizDialog Class

CNewWizDialog is derived from CDialog, but it is analagous to CPropertySheet when creating a standard MFC wizard. You create a dialog template with Back, Next, Cancel, and Finish buttons. You create a CNewWizDialog-derived class for your dialog resource.

The CNewWizPage Class

CNewWizPage is also derived from CDialog, however it is analagous to CPropertyPage when creating a standard MFC wizard. You create a dialog resource for each page of the wizard. You create a CNewWizPage-derived class for each wizard page.

CNewWizDialog and CNewWizPage have very similar class interfaces to CPropertySheet and CPropertyPage respectively, so it should not be too difficult to convert your existing wizards to windows 2000 style wizards.

Creating the Main Dialog

1. In the Visual C++ resource editor, create a dialog template for the main wizard window. Be sure to give it Next, Back, Finish, and Cancel buttons. Buttons should have the following identifiers to match the standard MFC wizard:

Placing the Cancel and Finish Buttons

In the traditional MFC wizards, The Finish button and the Cancel button are in the same position. One is hidden when the other is shown. If you want to mimic that behavior in these classes, it is simple enough, but you will have to make changes to
the CNewWizDialog::EnableFinish(BOOL bEnable) function to show are hide the proper buttons.

The Wizard Page Placeholder Control

You will also need to give your main wizard dialog a frame rectangle (picture) control to act as a placeholder. If you make the frame "etched", you will get a nicer appearance. Your dialog should look something like the one below.

2. Using ClassWizard, create a CDialog-derived class for your dialog resource. Change the base class from CDialog to CNewWizDialog. It is very important that you also change your message map to call the base class CNewWizDialog instead of CDialog as shown below. Otherwise, your wizards buttons will not work!

3. Override WM_INITDIALOG. Before calling the base class implementation of OnInitDialog, you must call CNewWizDialog::SetPlaceholderID() passing the control ID of the place holder rectangle as a parameter.

Be sure to call the base class implementation CNewWizDialog::OnInitDialog and not CDialog::OnInitDialog as show below:

BOOL CMasterDlg::OnInitDialog()
{
// you must call this function in OnInitDialog before you call the base class!
SetPlaceholderID(IDC_SHEETRECT);
// make sure to call the proper base class
CNewWizDialog::OnInitDialog();
return TRUE; // return TRUE unless you set the focus to a control
// EXCEPTION: OCX Property Pages should return FALSE
}

Creating the Wizard Pages

Follow the these steps for each page in your wizard.

1. Create a dialog resource for the page. The dialog should have the following properties:

Child Style

No Border

Not Visible

Disabled

Place the desired contols on the dialog.

Note: You should make each wizard page the same size as the placeholder on your main wizard dialog.

2. Use ClassWizard to create a CDialog-derived class for the dialog resource. Add any handlers for controls that you may require.

3. Change the base class from CDialog to CNewWizPage. It is very important that you change the base classes for OnInitDialog, the constructor, and the message map.

Putting it All Together

Once you have created all of your dialog classes as described above, you put the wizard together just as you would with a standard MFC CPropertySheet-based wizard. The only difference is that you must pass the ID of each pages's dialog resource to CNewWizDialog::AddPage().

Potential Problems

Many of the data validation functions for CNewWizPage return FALSE if the dialog data is not validated as desired. I did not spend a lot of time checking to make sure the classes worked as required when FALSE is returned from these functions. If you are doing a lot of data validation, be sure to test your wizard with invalid data so that it behaves as desired.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

Share

About the Author

Bob Pittenger is founder and President of Starpoint Software Inc. He holds a B.A. degree from Miami University, M.S. and Ph.D. degrees from Purdue University, and an MBA from Xavier University. He has been programming since 1993, starting with Windows application development in C++/MFC and moving to C# and .NET around 2005 and is a .NET Microsoft Certified Professional Developer.

Bob is the author of two books:
Billionaire: How the Ultra-Rich Built Their Fortunes Through Good and Evil and What You Can Learn from Them
and
Wealthonomics: The Most Important Economic and Financial Concepts that Can Make You Rich Fast.
Visit http://www.billionairebook.net for more information.

if i run the application than its works fine but after some its look like hang or its not responding.

i m checking the memory usage from task manager than i have noticed that in your application when i reached the last page of wizard the the memory of application constant.

but in my application after reached on last page,if i coninously click on back or next button than memory is still increase i think thats why it becomes not responding and looks like hang the applicaton.

please help me for this.

i m waiting for your reply.

thanks in advance.

IN A DAY, WHEN YOU DON'T COME ACROSS ANY PROBLEMS - YOU CAN BE SURE THAT YOU ARE TRAVELLING IN A WRONG PATH

hello,My name's VanPhuong.I come from Vietnam.
Can you help me?
now i'm studying a subject plan in Programming Windows.Its name is design in interface Windows Explorer(My Computer).Now i'm a student in physics at University of Natural Sciences.So i don't understand clearly in VC++.Because i hope you'll send me the code of Windows Explorer which is written in VC++ in SDI.

I am getting the memory assertion whenever i am trying to Fill the wizarad's control values in the structures and trying to print these values .For the first iteration it sounds good,but when i call the wizarad for second time ,it is giving the assertion in the below line
if (Dlg.DoModal() == ID_WIZFINISH)

This class is not needed to create Windows 2000 style wizards. CPropertySheetEx and CPropertyPageEx already support this look and feel. In fact you can use any color background - white, pink... Also, XP theming can be difficult with this class for extension DLL - you need activation contexts for each dialog. With CPropertySheetEx you only need an activation context for the sheet.

How to achieve without this class:

Derive from CPropertySheedEx
Create a Property Page header image that is 49x49 pixels (not standard 48x48). Set the 49th pixels to white (or any color you want the background).
Construct with watermark and header images.
Set PSH_WIZARD97 style

You can hide the header in your CPropertyPageEx defined classes by using the PSP_HIDEHEADER style.

Also 'Another big advantage of this method of creating wizards is that you have total control over the placement and text of the wizard buttons, and you can even add other control outside of the wizard pages.' can be achieved very easily with CPropertySheet...

- create a range of user-defined message
- add a handler to the parent dialog that handles the message

And do this for all of the child dialogs:

- define a data retrieval message ID and a data submission message ID

- Create a handler in the base class for the child dialogs that handles these messages

- use PostMessage to post the message to the parent with the following parameters:

WPARAM - MESSAGE_FOR_CHILD (this might signal a request for data or submission of new data)

LPARAM - pointer to variant_t which contains accepts the data.

- have the parent reflect the MESSAGE_FOR_CHILD to each of the child dialogs.

---------------------------

This method prevents each child dialog from having to know what other child dialogs are available. If the message isn't handled, the parent dialog can take some action (like sending a message to all child dialogs informing them that the message wasn't handled.

Hi; I'm trying to create a CComboBox dynamically in a OCX. I can do it and it draws itself when the OCX is drawn. But, desptite a CEdit control works correctly in the same OCX, the CComboBox enters in a loop of redrawings of itself. The CComboBox works correctly if I create it in a simple MFC dialog application. But the same effect happens if I try to create the combobox inside a CListCtrl. In this case, in addition, the combo is clipped (but this is the normal behaviour of a list control...).
I've thought that that problem could be caused by the parent passed when created, but I don't know:

Hello,
i tried to use the above method with VC++6.0
i am quite new to this. i added NewWizDialog.cpp and NewWizDialog.h like this: file/new/Files C++ Source File & C++ Header file. samething for NewWizPage.cpp & for .h.

in classview tab it shows the above 2 classes. however it is not appearing in classwizard classes.

in the projects folder you'll see a file called .clw where is whatever your project is called. Either way, there should only be one .clw file in there. Delete it and hit ctrl-w (to get the class wizard back). It'll say it can't find the file and do you want to rebuild it...say yes. Then those'll show up. I spent like 4 hrs running through msdn docs to find that b/c i was searching the wrong topic...HOWEVER...here's a link to the document. i feel your pain...dont' worry

I want to jump to next page without the user clicking the next button. e.g I am doing a wizard that checks if some components are installed on my thirdpage so when the operation completes I want to advance to the next page automatically which dispalys the result.

Hello.,
Thanks for the great arictle.!
I have 7 pages in my wizard . How do I jump directly from 2nd page to 5th Page by skipping 3rd & 4th Page.?
I tried with SetActivePage() , in OnWizardNext() of 2nd Page ..it doesn't work..

I solved it !
The problem is the OnOK - when U press Enter, by default the OnOK is called.
Just Override your OnOK function of the page and remark the default call to CDialog::OnOK or CPropertyPage::OnOK or whatever.
It will work.
Good Luck,
janiv.

An excellent article and very easy to implement. Thanks you. One question though.

Is it possible to disable the finish button when the final page appears. I have tried EnableFinish(FALSE) and parent->GetDlgItem(ID_WIZFINISH)->EnableWindow(FALSE) and the Finish button refuses to be inactive.

I can inactivate the finish button once the final page has displayed, but I have to manually do it (by pressing a temporary button) after the final page displays.

Perhaps I am trying to inactivate the finish btn in the wrong place. So far I've tried OnInitDialog and OnSetActive().