Introduction
Unfortunately, Microsoft didn't include a wizard for us in .NET, nor do I see any plans to implement one before late 2003, so it's necessary for us to create a workaround that is both elegant and flexible.
I want to, first off, give James Johnson credit for inspiring me to write a wizard control and post it for you here on CodeProject. Although his solution is a very usable, I didn't find it as elegant as I wished. It used the standard model of having multiple pages separated into multiple files, and if your system is like mine, the toolbox started to get quite crowded with inherited controls. Thanks James for taking up precious time to give us a solution. I based my example off of his sample, so it might look a tad like his.
Traditionally, wizards were created from property pages/sheets with a few additional buttons and setting SetWizardMode
for turning off the tabs. I tend to like both a next/finished button instead of renaming the next button on the last step. If there are circumstances where I can finish up a wizard early, then the single button approach has no solution.
Problems
Well, we don't have SetWizardMode
anymore! Nor can we turn off the tabs from appearing on the property sheets!! That created a big problem in trying to make a property sheet based wizard functional. But, the property sheets became incredibly more powerful with the ability to work on all the pages simultaneously.
So, I cheated...
Solution
The first thing I had to do was turn off those darn tabs!! After months of searching, I didn't get too far with any type of solution. I pray that someone has a better solution than this. Then I can come back and edit it as fast as possible to hide what I did.
I created a simple control called EtchedLine
. It's actually a decent little control that draws a simple line the width of the control. Then whenever the base form is resized, I moved the EtchedLine
over onto the top of the tab control. I changed the drawing property of the control to draw a white line on the bottom of itself to completely hide where the tabs were and to make it look like one continuous window. Quit laughing.
private void InitializeEtchedLine( )
{
if( this.DesignMode )
{
lblEtchedLine.Size = new Size ( 10, 10 );
lblEtchedLine.Location = new Point( 10, Height - 10 );
}
else
{
lblEtchedLine.Size = new Size( WizardControl1.Width - 1,
WizardControl1.DisplayRectangle.Y - 1 );
lblEtchedLine.Location = WizardControl1.Location;
}
}
WizardControl
Once I was allowed to use the property sheets, the WizardControl
began to evolve nicely. I have written a base form named WizardForm
that completely encompasses the entire control. So don't worry about having to access any of the WizardControl
properties directly.
Properties
NextButton
- The form's next button. The control will enable/disable the button when appropriate. BackButton
- The form's back button CancelButton
FinishedButton
TitleLabel
- The main title of the page. The control will update the title depending on the step. AllowBack
- Allows the user to use the back button to edit their previous choices
Methods
MoveBack
- Step back one page MoveNext
- Step forward one page MoveFirst
- Go to the first page and initialize itself MoveTo
- Go to a specific index or page HideStep
- Hide a step from the standard page order ShowStep
- Show a hidden step in the standard page order
Events
Event_Initialize
- When the wizard is first initialized. Reset/set any options here. Event_MoveNext
- Next button has been pressed. Override to validate the page. Event_MoveBack
- Previous button has been pressed. Event_PageChanged
- Page has changed to this current page. Override to initialize variables Event_Cancelled
- Cancel button was pressed. Override to warn if appropriate. Event_Finished
- Finish button was pressed. Override to validate all of the data on the form.
WizardTabPage
You will be adding pages to the WizardControl
the exact same way that you would be adding pages to a TabControl
.
Important: Once you have added a TabPage
to your control, you will currently need to edit the source code and change all occurrences of TabPage
to the WizardTabPage
. I'm sure there's some way of doing that automatically through an attribute, and if you find out how, please let me know!
this.tabPage2 = new WizardTab.WizardTabPage();
Properties
Title
- The text that you would like to show on the top of the page ShowFinished
- Show the finished button on this page regardless of whether it is the last page or not Hidden
- Hide this property page based on user decisions
You can change the properties of the tab page directly either through the Property
settings or through the TabPages
property in the WizardControl
.
WizardForm
And the last piece of the wizard is the form that you will be using to inherit from.
Important: To initialize the page correctly, we need to add a MoveFirst
to your base class right after the data is initialized. This will initialize your next/prev buttons correctly.
public OrderPizza( )
{
InitializeComponent( );
this.WizardControl1.MoveFirst( );
}
Virtual Functions
OnWizInitialize
OnWizMoveNext
OnWizMoveBack
OnWizPageChanged
OnWizCancelled
OnWizFinished
Inheriting from WizardForm
Ok, you're ready to add a wizard to your project.
- Add
WizardControl
and EtchedLine
to your toolbar. - Add an Inherited Form and derive from
WizardTab.WizardForm
. - Add additional pages by modifying the control's
TabPages
. - Don't forget to rename all
TabPages
WizardTabPages
in the source code. - Add
MoveFirst
after your InitializeComponents
function in your main class.
That's really about it!!
Validating Pages
To validate the data on the page, override the OnWizMoveNext
function. Put any type of validation that you wish and pass eCancel.Cancel = false
if you want to prevent the user from continuing.
protected override void OnWizMoveNext( WizardTab.WizardControl pWizard,
WizardTab.WizardTabPage pTabPage,
CancelEventArgs eCancel )
{
switch( pWizard.SelectedIndex )
{
case 1:
if( this.chkAnchovies.Checked )
{
MessageBox.Show( "We've never actually had anchovies. It's just something " +
"we've always put on our menu", "Sorry, no anchovies" );
eCancel.Cancel = true;
}
if( this.chkBacon .Checked == false && this.chkMushrooms.Checked == false &&
this.chkOnions.Checked == false && this.chkPepperoni.Checked == false )
{
MessageBox.Show( "You've got to put something on your pizza, that just ain't " +
"American", "What? No toppings?!" );
eCancel.Cancel = true;
}
break;
}
return;
}
Using the Data
What's really nice about this solution is that all of your variables are accessible in your functions. All of the variables on your pages can be accessed directly without the need to pass your variables via a parent object or through function parameters.
protected override void OnWizPageChanged( WizardTab.WizardControl pWizard,
WizardTab.WizardTabPage pTabPage )
{
switch( pWizard.SelectedIndex )
{
case 3:
StringBuilder strOrder = new StringBuilder( );
strOrder.Append( "Crust: \r\n" );
if( this.btnThickCrust .Checked )
strOrder.Append( "\tThick Crust\r\n" );
if( this.btnThinAndCrispy.Checked )
strOrder.Append( "\tThin and Crispy\r\n" );
if( this.btnChicagoStyle .Checked )
strOrder.Append( "\tChicago Style Deep Dish\r\n" );
strOrder.Append( "\r\nToppings: \r\n" );
if( this.chkPepperoni.Checked )
strOrder.Append( "\tPepperoni\r\n" );
if( this.chkBacon .Checked )
strOrder.Append( "\tCanadian Bacon\r\n" );
if( this.chkMushrooms.Checked )
strOrder.Append( "\tMushrooms\r\n");
if( this.chkOnions .Checked )
strOrder.Append( "\tOnions\r\n" );
this.textBox1.Text = strOrder.ToString( );
break;
}
}
Conclusion
I hope that I've included enough flexibility that you find this Wizard a superior solution to the old MFC wizards that we're all familiar with. Let me know if there is more functionality that you would like to see.
Oh yea, The Pizza Hut name, logos, and related marks are trademarks of Pizza Hut, Inc.
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. A list of licenses authors might use can be found here.