A Reusable Wizard Component

Environment: .NET, C#, VS.NET, Win2K

Introduction

A few months back, I was given the task of porting an MFC application to .NET using C#. It surprised me to find no direct support for wizard dialogs in .NET. There were property pages in the form of the unwieldy TabControl class, but no dual functionality as I had come to expect from CPropertySheet and CPropertyPage. I think it was an even bigger surprise when I did a search for wizard dialogs implemented in .NET and came up empty. Am I the only one still using wizards? Is there some really easy way to do it in .NET that I completely overlooked? Whatever the answer, I resigned myself to write a reusable wizard component in .NET that mimicked, as closely as possible, the MFC implementation.

My goals for this project were the following:

  1. Be able to create wizards programatically in a manner similar to what I had become accustomed to using MFC, and
  2. Be able to use the Windows Form Designer to create individual wizard pages in a manner similar to how I was able to create them by using the dialog editor.

This article presents two classes that enable you to create wizards in .NET in much the same way they are created using MFC. Two additional classes make it easy to implement Wizard 97-style wizards, as well.

The Classes

The first two classes to be discussed, WizardForm and WizardPage, are meant to correspond to CPropertySheet and CPropertyPage, respectively. The remaining two classes, ExternalWizardPage and InternalWizardPage, are merely helper classes to assist in building Wizard 97-style wizards.

WizardForm

WizardForm is derived from Form and provides the basic mechanisms for managing a collection of WizardPage instances. Additionally, it provides default the functionality and UI layout for the Back, Next, Finish, and Cancel buttons. The wizard-related methods of CPropertySheet, such as SetWizardButtons, are implemented, as well. A brief summary of each method this class introduces is presented below.

    public void SetFinishText( string )
    Sets the text for the Finish button.
    public void SetWizardButtons( WizardButtons )
    Enables or disables the Back, Next, and Finish buttons in the wizard.

The first thing to do when creating your own wizard is to extend this class, typically by using the Add Inherited Form command within VS.NET. You may edit the properties of the inherited controls and add additional controls beyond the standard wizard buttons if you want.

WizardPage

WizardPage is derived from UserControl. The wizard-related methods of CPropertyPage, such as OnSetActive, OnWizardNext, and so forth, are implemented. A brief summary of each property/method this class introduces is presented below.

    protected WizardForm Wizard { get; }
    Gets the WizardForm to which this WizardPage belongs.
    protected internal virtual bool OnKillActive()
    Called when this page is no longer the active page. Do data validation tasks here.
    protected internal virtual bool OnSetActive()
    Called when this page becomes the active page.
    protected internal virtual string OnWizardBack()
    Called when the Back button is clicked. Return WizardForm.DefaultPage to advance to the previous page, WizardForm.NoPageChange to prevent the page from changing, or the value of the Name property of the page to be displayed.
    protected internal virtual string OnWizardNext()
    Called when the Next button is clicked. Return WizardForm.DefaultPage to advance to the next page, WizardForm.NoPageChange to prevent the page from changing, or the value of the Name property of the page to be displayed.
    protected internal virtual bool OnWizardFinish()
    Called when the Finish button is clicked. Return true to close the wizard or false to prevent the wizard from closing.

Once your custom WizardForm is complete, you create one or more WizardPage-derived classes. You create your own page by extending this class, or any class derived from it, typically using the Add Inherited Control command within VS.NET. Add whatever combination of controls you would like to be displayed on this page. To associate the page with its wizard, you add it to the Controls collection of the appropriate WizardForm.

ExternalWizardPage

ExternalWizardPage extends WizardPage and represents an external (for example, the first or last) page in a Wizard 97-style wizard. It merely provides the required controls with the proper positioning and properties to satisfy the Wizard 97 specifications.

InternalWizardPage

InternalWizardPage extends WizardPage and represents an internal (for example, neither the first nor last) page in a Wizard 97-style wizard. Again, it merely provides the required controls with the proper positioning and properties to satisfy the Wizard 97 requirements.

Using WizardForm and WizardPage in Your Application

Any time you want to add a WizardPage-derived class to your project, use the Add Inherited Control command. Select WizardPage (or any other WizardPage-derived class, such as ExternalWizardPage or InternalWizardPage) as the component to inherit from, and add controls to the page as desired.

To add the wizard itself to your project, use the Add Inherited Form command. Select WizardForm (or any other WizardForm-derived class) as the component to inherit from. You may add additional controls and behavior if you want. The wizard pages can be added to the form in the constructor, for example,

  public MyWizardForm()
  {
      // Some initialization code here
      //     .
      //     .
      //     .

      Controls.AddRange( new Control[] {
          new MyFirstWizardPage(),
          new MySecondWizardPage(),
          new MyThirdWizardPage()
      } );
  }

Note that you can directly use the WizardForm and WizardPage classes without deriving your own classes, but in most cases it's a very inelegant solution.

Summary

This article has described a set of two reusable classes for implementing wizards within the .NET environment. Two additional classes are provided to assist in implementing wizards that must conform to the Wizard 97 specification.

There are bound to be some areas I've overlooked in this implementation. For instance, I have not tested these classes when the WizardForm is acting as the main window of an application (for example, it was passed as the Form argument to Application.Run). Also, the code is not localized. However, the source code should serve as a good starting point for developing your own, possibly more robust, .NET wizard implementations.

Downloads

Download source and demo project (unified solution) - 64 KB


Comments

  • Sharing data among all user controls in the wizard

    Posted by Dave Lucre on 10/29/2012 04:18pm

    In reply to: "Interaction Posted by oystagoymp on 01/25/2007 09:34am How would I load, let's say the contents of a textbox on the second page into a variable on the third page" I came here looking for an answer to the same question. I guess using a static class and static variables would be one way to go. I chose to pass a class reference through to the constructor on each user control. Sharing a common class between all of them. e.g: Create a common class: public class commonClass { public string whateverString { get; set; } } Then on your wizard form, pass the common class through to each user control in it's constructor: public commonClass cc = new commonClass(); public MyWizardForm() { InitializeComponent(); Controls.AddRange( new Control[] { new FirstPage(cc), new SecondPage(cc), new ThirdPage(cc), new FourthPage(cc) } ); Then on your user controls assign it to a private local variable: private commonClass _cc; public FirstPage(commonClass cc) { InitializeComponent(); _cc = cc; } Finally, in your user controls you will have access to the _cc.whateverString value across all of them.

    Reply
  • Saving variable values

    Posted by Dave Lucre on 10/29/2012 04:17pm

    In reply to: "Interaction Posted by oystagoymp on 01/25/2007 09:34am How would I load, let's say the contents of a textbox on the second page into a variable on the third page" I came here looking for an answer to the same question. I guess using a static class and static variables would be one way to go. I chose to pass a class reference through to the constructor on each user control. Sharing a common class between all of them. e.g: Create a common class: public class commonClass { public string whateverString { get; set; } } Then on your wizard form, pass the common class through to each user control in it's constructor: public commonClass cc = new commonClass(); public MyWizardForm() { InitializeComponent(); Controls.AddRange( new Control[] { new FirstPage(cc), new SecondPage(cc), new ThirdPage(cc), new FourthPage(cc) } ); Then on your user controls assign it to a private local variable: private commonClass _cc; public FirstPage(commonClass cc) { InitializeComponent(); _cc = cc; } Finally, in your user controls you will have access to the _cc.whateverString value across all of them.

    Reply
  • Wizards

    Posted by Michal Kubiak on 07/22/2012 11:50pm

    I found a solution on msdn : http://msdn.microsoft.com/en-us/library/7k3w6w59(v=vs.100).aspx . But your framework looks much much nicer and easier to understand.

    Reply
  • Interaction

    Posted by oystagoymp on 01/25/2007 09:34am

    How would I load, let's say the contents of a textbox on the second page into a variable on the third page

    Reply
  • Nice work

    Posted by Legacy on 12/18/2003 12:00am

    Originally posted by: Arindam Pal

    Thats a nice piece of work Steve. You saved lot of our time in developing our own wizard framework in .NET.

    Reply
  • Good example! Easy to understand.

    Posted by Legacy on 11/08/2003 12:00am

    Originally posted by: Peter Gloor

    I almost didn't believe that there is nothing provided in VS to let me quickly create a Wizard.

    I'm glad having found this Wizard example. It's just what I need. The other examples I found were either too complex and complicated or didn't work.


    Reply
  • Great stuff!

    Posted by Legacy on 11/06/2003 12:00am

    Originally posted by: Bruce Cutler

    I can't figure out why Microsoft left out this feature. I'm glad you have created this control.

    Reply
  • Great Information

    Posted by Legacy on 10/22/2003 12:00am

    Originally posted by: Dan Menear

    You just saved me a lot of time looking around the web for a solution on building wizards in c#... thanks!

    Reply
  • Like the solution - one small question

    Posted by Legacy on 09/29/2003 12:00am

    Originally posted by: Dave G.

    Definately a nice approach - quick question though on initialization of the controls for the interior pages.

    When the InitializeComponent() method is called like most C# programs it is adding the controls to the various settings in the application. These are all hard coded without a designer equivalent - is there any way to use a form that is designed with form designer and loading/attaching into the Wizard page control at run time.

    Obvious reason for question, it's possible our design department will change the design once the work is done, could be very time consuming to make changes to the code. If I'm missing something, please let me know.

    Thanks for all of the hard work.

    Reply
  • Excellent.....A lifesaver!!!

    Posted by Legacy on 07/23/2003 12:00am

    Originally posted by: Gerry Christiansen

    Just wanted to add my two cents...this is a very good and much needed tutorial. I've looke all over MSDN docs and Microsoft's website for this type of example, but there was nothing...

    Thanks for your work and contribution..

    - Gerry

    Reply
  • Loading, Please Wait ...

Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • Learn How A Global Entertainment Company Saw a 448% ROI Every business today uses software to manage systems, deliver products, and empower employees to do their jobs. But software inevitably breaks, and when it does, businesses lose money -- in the form of dissatisfied customers, missed SLAs or lost productivity. PagerDuty, an operations performance platform, solves this problem by helping operations engineers and developers more effectively manage and resolve incidents across a company's global operations. …

  • Today's agile organizations pose operations teams with a tremendous challenge: to deploy new releases to production immediately after development and testing is completed. To ensure that applications are deployed successfully, an automatic and transparent process is required. We refer to this process as Zero Touch Deployment™. This white paper reviews two approaches to Zero Touch Deployment--a script-based solution and a release automation platform. The article discusses how each can solve the key …

Most Popular Programming Stories

More for Developers

RSS Feeds