UPDATE: 2018-05-17
This was originally published 8 years ago. Since then, CodePlex has been discontinued. As such, I have moved the code to GitHub for anyone who still finds this useful. Simply click the new download link at the top of this article.
Introduction
Looking for a quick and simple way to create a wizard host? Look no further. SimpleWizard provides the required interface for your wizard pages, the navigation and even a basic host. A little work is needed on the host, as its UI is rather plain, but the subject of the article is really how to handle the navigation, etc. I'll leave all the prettying up to the readers.
The Interface
public interface IWizardPage
{
UserControl Content { get; }
void Load();
void Save();
void Cancel();
bool IsBusy { get; }
bool PageValid { get; }
string ValidationMessage { get; }
}
This is the basic requirement: an interface that you can use for your wizard pages. Just create some user controls (the pages), inherit IWizardPage
and away you go for that step.
The Collection & Navigation
public enum WizardPageLocation
{
Start,
Middle,
End
}
public class WizardPageCollection : Dictionary<int, IWizardPage>
{
public IWizardPage CurrentPage { get; private set; }
public IWizardPage FirstPage { get; }
public IWizardPage LastPage { get; }
public WizardPageLocation PageLocation { get; private set; }
public bool CanMoveNext { get; }
public bool CanMovePrevious { get; }
public WizardPageCollection()
{
PageLocation = WizardPageLocation.Start;
}
public delegate void
WizardPageLocationChangedEventHanlder(WizardPageLocationChangedEventArgs e);
public event WizardPageLocationChangedEventHanlder WizardPageLocationChanged;
public IWizardPage MovePageFirst()
public IWizardPage MovePageLast()
public IWizardPage MovePageNext()
public IWizardPage MovePagePrevious()
public int IndexOf(IWizardPage wizardPage)
public void Reset()
private void NotifyPageChanged(int previousPageIndex)
}
public class WizardPageLocationChangedEventArgs
{
public WizardPageLocation PageLocation { get; set; }
public int PageIndex { get; set; }
public int PreviousPageIndex { get; set; }
}
Here you can see that I only provided the signatures of the methods, etc. This is for clarity. You can find the actual implementation in the source code at http://simplewizard.codeplex.com/.
For now, to get more of an idea of what is going on in the MovePage...()
methods, check out MovePageNext()
here:
public IWizardPage MovePageNext()
{
int previousPageIndex = IndexOf(CurrentPage);
if (PageLocation != WizardPageLocation.End &&
CurrentPage != null)
{
int nextPageIndex = (from x in this
where x.Key > IndexOf(CurrentPage)
select x.Key).Min();
int lastPageIndex = (from x in this
select x.Key).Max();
if (nextPageIndex == lastPageIndex)
{
PageLocation = WizardPageLocation.End;
}
else { PageLocation = WizardPageLocation.Middle; }
CurrentPage = this[nextPageIndex];
NotifyPageChanged(previousPageIndex);
return CurrentPage;
}
return null;
}
You'll notice we are inheriting from Dictionary<int, IWizardPage>
. The key is the page number and the Value is an IWizardPage
. Simply inheriting the aforementioned dictionary takes care of all the collection stuff for us nicely. Now, all we have to care about is navigation.
There are some properties giving you direct access to the current page, first page and last page in the collection, as well as some properties that will tell you whether it is possible to move forward or not and the methods are letting you actually move back and forth through the pages (if possible). The only real place you're going to need any of this is in your host form, which we'll look at in a little while...
I think that sums that up nicely.
Wizard Host
public partial class WizardHost : Form
{
private const string VALIDATION_MESSAGE = "Current page is not valid.
Please fill in required information";
public WizardPageCollection WizardPages { get; set; }
public bool ShowFirstButton { get; set; }
public bool ShowLastButton { get; set; }
public bool NavigationEnabled { get; set; }
public delegate void WizardCompletedEventHandler();
public event WizardCompletedEventHandler WizardCompleted;
public WizardHost()
{
InitializeComponent();
WizardPages = new WizardPageCollection();
WizardPages.WizardPageLocationChanged += new
WizardPageCollection.WizardPageLocationChangedEventHanlder
(WizardPages_WizardPageLocationChanged);
}
void WizardPages_WizardPageLocationChanged(WizardPageLocationChangedEventArgs e)
{
LoadNextPage(e.PageIndex, e.PreviousPageIndex, true);
}
private void NotifyWizardCompleted()
private void OnWizardCompleted()
public void UpdateNavigation()
private bool CheckPageIsValid()
public void LoadWizard()
public void LoadNextPage(int pageIndex, int previousPageIndex, bool savePreviousPage)
private void btnFirst_Click(object sender, EventArgs e)
private void btnPrevious_Click(object sender, EventArgs e)
private void btnNext_Click(object sender, EventArgs e)
private void btnLast_Click(object sender, EventArgs e)
}
And this is where all the work goes on. Here you have the WizardPageCollection
, some properties to choose whether or not to display the First And Last buttons on the wizard (it wouldn't make sense to not show the Next and Previous buttons, which is why only the 2 properties).
All the navigation updates happen here as well as the checks to see whether the current page is valid before moving to the next one. CheckPageIsValid()
calls the PageValid
property on the IWizardPage
. If it returns false
, then a message box is shown along with the page's ValidationMessage
property.
Again, see the source code for further details.
When you are ready to start your wizard, all you need do is add your wizard pages and show the wizard host as a dialog. Here is an example:
private void button1_Click(object sender, EventArgs e)
{
WizardHost host = new WizardHost();
host.Text = "My Wizard";
host.WizardCompleted +=
new WizardHost.WizardCompletedEventHandler(host_WizardCompleted);
host.WizardPages.Add(1, new Page1());
host.WizardPages.Add(2, new Page2());
host.WizardPages.Add(3, new Page3());
host.LoadWizard();
host.ShowDialog();
}
Conclusion
SimpleWizard
allows you to forget about all the navigation issues and simply add your user controls to a WizardHost
instance. As simple as that.
History
- 26th October, 2010: Initial post