Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / XML

AlfaWizard Library

4.61/5 (15 votes)
26 Sep 2008CPOL5 min read 1   315  
AlfaWizard is a simple library for the purpose of making wizards.
introPage.jpg

Introduction

The AlfaWizard library is a simple library for the purpose of making wizards. This library allows making a full wizard with just a few lines of source code and with a special *.xml configuration file. This XML file helps in reducing source code lines, because it sets properties for individual pages. Attributes of pages can also be set from the program. The base element of the whole wizard is the main window in which individual pages can be changed according to user actions. The user doesn't have direct access to the main wizard window, but can change the properties through the main wizard class, WizardDialog.

Background

The wizard behavior can be compared to a finite state machine (FSM). Every page represents a state in an automata. Between individual states, we move with the help of inputs (user inputs). Each page (state of FSM) has some input value, which can be seen from outside. Since an FSM and a simple wizard are so similar, I decided to implement my library with a behavior closest to an FSM. So, it’s possible to choose a wizard page depending on user input.

AlfaWizard1.jpg

Using the Code

In the making of the AlfaWizard library, my intention was to reduce the number of lines in the source code which the user must write. This was fulfilled with the use of the XML configuration file. The various XML elements are used by BasePage, from which all the pages used in a wizard must be derived. In the attached test project, in the class TestClass, you can see how the user just needs a very small number of lines to make a good wizard. The library also contains a set of exceptions which can help in the debugging phase of an application.

NameDescription
BaseExceptionAll other AlfaWizard exceptions are derived from this basic exception BaseException. This exception has no additional data.
InitExceptionThrown during the initialization process of a page.
PageInsertExceptionThis exception is thrown when you try to insert a non-initialized page into WizardDialog.
ParseExceptionThis exception is thrown when some exception is thrown when reading the XML configuration file.
PageNotFoundExceptionThis exception is thrown when you try to use a page that is not in the WizardDialog.

In the test project, you can also find how to derive your own page and use it in the wizard. The pages already available for the wizard are thread safe. In the case of transferring data between pages, I use some interfaces like IDirBrowse or ILicenseAccept. For example, to get in the ProgressPage path where the zip file should by unzipped, we do:

C#
//automatically jumps to next page if it’s a 100% on progress bar
progress.AutoJumpNext = true;
//id of the page that must implement IDirBrowse, 
//from which you get a path where zip archive is unzipped
progress.BrowsePageId = browse.Information.Id;
//operation that starts in separate thread after the page is shown
progress.ProgDeleg = WorkingTest;

Example TestClass

C#
public class TestClass
{
    private readonly string dirPath;
    private WizardDialog wd;
    private IntroPage intro;
    private LicensePage license;
    private FinalPage final;

    private TestPage test;
    private AlfaPage alfa;
    private BetaPage beta;

    public TestClass ()
    {
        dirPath = Path.GetDirectoryName(
          Assembly.GetExecutingAssembly().GetModules()[0].FullyQualifiedName);
        //create object WizardDialog, where 2<sup>nd</sup> argument in constructor is language
        wd = new WizardDialog(Path.Combine(dirPath, "configFile.xml"), "en");

        //create object intro page, 1<sup>st</sup> argument is object type of WizardDialog, 
        //2<sup>nd</sup> is page name in XML file, 3<sup>rd</sup> is allowed wizard buttons
        intro = new IntroPage(wd, "My Intro Page", WindowButtons.NextCancel);

        license = new LicensePage(wd, "My license page", WindowButtons.BackNextCancel);
        test = new TestPage(wd, "My own test page", WindowButtons.BackNextCancel);
        final = new FinalPage(wd, "Designer final page", WindowButtons.BackNextCancel);
        alfa = new AlfaPage(wd, "AlfaPage", WindowButtons.BackNextCancel);
        beta = new BetaPage(wd, "BetaPage", WindowButtons.BackNextCancel);

        //now we specify previous and next page for intro page
        intro.Initialize(null, license); 

        license.Initialize(intro, test);
        PageInformationCollection testBack = new PageInformationCollection();
        testBack.Add(license.Information);
        PageInformationCollection testNext = new PageInformationCollection();
        testNext.Add(alfa.Information);
        testNext.Add(beta.Information);
        //now we specify one previous page and two next pages for test page
        test.Initialize(testBack, testNext);

        alfa.Initialize(test, final);
        beta.Initialize(test, final);
        final.Initialize(test, null);

        //after page initialization you must insert page 
        //into WizardDialog.Pages collection
        wd.PageAdd(intro);
        wd.PageAdd(final);
        wd.PageAdd(license);
        wd.PageAdd(test);
        wd.PageAdd(alfa);
        wd.PageAdd(beta);
    }

    public void Run()
    {
        //now we show wizard
        wd.ShowWizard();
    }
}

How To Create Your Own Page

In case you want to create your own page for a wizard without added functionalities, you just need to create a new control and add this code to it:

C#
public partial class AlfaPage : BasePage
//control inherit from BasePage
{
    //this constructor is mandatory
    public AlfaPage()
    {
        InitializeComponent();
    }

    //this constructor is mandatory
    public AlfaPage(WizardDialog wd) : base (wd)
    {
        InitializeComponent();
    }

    //this constructor is mandatory
    public AlfaPage(WizardDialog wd, string name) : base(wd, name)
    {
        InitializeComponent();
    }

    //this constructor is mandatory
    public AlfaPage(WizardDialog wd, string name, WindowButtons dialogButtons)
         : base(wd, name, dialogButtons)
    {
        InitializeComponent();
    }
}

Here is a description of all the important methods that are available in the BasePage class. In case you want to override some methods, you must always call the base method first. The base method invokes events that could be processed and to which the wizard reacts.

Method name (all methods are public, parameters are skipped)Description
bool InitializeThis method is called by the page initialization where the predecessors and followers are stated. In this method, page settings are read from the configuration file.
PageInformation RunBackThis method is called when the user clicks on the “Back” button.
PageInformation RunNextThis method is called when the user clicks on the “Next” button.
bool WorkBeforeShowThis method is called before the page is shown in the wizard window.
bool WorkAfterShowThis method is called after the page is shown in the wizard window.
bool WorkBeforeBackThis method is called before replacing the actual page with the previous page.
bool WorkBeforeNextThis method is called before replacing the actual page with the next page.
bool WorkCancelThis method is called when the user closes the wizard window. This method is called to cascade all the previous pages of the wizard.
string ToStringDo not override this method if you want to use the WizardLogger class.

An Example of the XML Configuration File

The included example contains a configuration file, which is used for setting the attributes of individual wizard pages. The file is named configFile.xml and its base structure looks like this:

XML
<?xml version="1.0" encoding="utf-8" ?>
    <Wizard> 
      <WizardDialog>
       <Language type="en">
              <BackButtonName>Back</BackButtonName>
              <NextButtonName>Next</NextButtonName>
              <FinishButtonName>Finish</FinishButtonName>
              <CancelButtonName>Cancel</CancelButtonName>
              <CancelWizardMsg>Do you want cancel this dialog?</CancelWizardMsg>
          <WizardHeaderMsg>Warning</WizardHeaderMsg>
        </Language>
      </WizardDialog>
      <IntroPage name="My Intro Page">
        <Language type="en">
         <Purpose>This is purpose of dialog</Purpose>
          <WindowTitle>Window title 1</WindowTitle>
          <ActionName>Action name 1</ActionName>
          <ActionDescription>Action description 1</ActionDescription>
          <HeaderImagePath></HeaderImagePath>
        </Language>
      </IntroPage>
      <LicensePage name="My license page">
        <Language type="en">
          <WindowTitle>Window title 2</WindowTitle>
          <ActionName>Action name 2</ActionName>
          <ActionDescription>Action description 2</ActionDescription>
          <HeaderImagePath></HeaderImagePath>
          <LicenseText>test</LicenseText>
          <!--<LicenseText type="rtf">paste rtf document here</LicenseText>-->
          <Accept>I agree license agreement</Accept>
          <Refuse>I not accept license agreement</Refuse>
          <NotValidText>You must accept license agreement 
                        before continue!</NotValidText>
        </Language>
      </LicensePage>
      <FinalPage name="Designer final page">
        <Language type="en">
          <WindowTitle>Window title 4</WindowTitle>
          <ActionName>Action name 4</ActionName>
          <ActionDescription>Action description 4</ActionDescription>
          <HeaderImagePath></HeaderImagePath>
        </Language>
      </FinalPage>
      <TestPage name="My own test page">
        <Language type="en">
          <WindowTitle>Window title 3</WindowTitle>
          <ActionName>Action name 3</ActionName>
          <ActionDescription>Action description 3</ActionDescription>
          <HeaderImagePath></HeaderImagePath>
        </Language>
      </TestPage>
    </Wizard>

Future Development

Considering that the user has no access to the main application window, I plan in future to bring all element properties of the main window to the class WizardDialog and also to the configuration file.

History

  • 21.08.2008 - First version of article
  • 30.08.2008 - New library version (0.0.1.0) see in changelog

Change Log

  • Version 0.0.0.1 released 21.08.2008
    • First official version of library
  • Version 0.0.1.0 released 30.08.2008
    • Changed return values to void of methods WizardDialog.RunBack() and WizardDialog.RunNext()
    • New readonly attribute PreviousPage, which helps you to get back page especially (back from state 6 to state 4 or 5)
    • New WizardDialog constructor, WizardDialog(Stream xmlStream, string language)
    • Removed method ReadFromFile(string path, string xpath)
    • New method ReadFromXml(Stream xmlStream, string xpath) which replaces old method ReadFromFile

Special Thanks

I want to say thanks to all the people that have helped me with this article. In the first place, thanks to Ing. Ondrej Plocica for the theoretical help and advice. Secondly, thanks to Ing. Peter Gregan for helping me with the translation.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)