Code-Only: Winforms Wizard Series Article 4 (C#)

using System;
using System.Collections;
using System.ComponentModel;
using System.Threading;
using System.Windows.Forms;

public interface IWizardDialog {
    Control NavigatePrevious { get; }
    Control NavigateNext { get; }
    Control NavigateFinish { get; }
    Control UIRoot { get; }
    bool Display { get; set; }
}

public interface IWizardPanel {
    bool ShowNavigatePrevious { get; }
    bool ShowNavigateNext { get; }
    bool ShowNavigateFinish { get; }
}

public interface IWizardNavigation {
    bool SupportNamedNavigation { get; }
    string NavigateName { get; }
    string NavigatePreviousTarget { get; }
    string NavigateNextTarget { get; }
}

public class WizardController : ApplicationContext {
    private bool complete = false;
    private int wizardIndex = -1;
    private IWizardDialog wizardDialog = null;
    private ArrayList wizardPanels = new ArrayList();
    private Hashtable nameMap = new Hashtable();

    public WizardController() { }
   
    public void SetDialog(Form dialog) {
        IWizardDialog wDialog = dialog as IWizardDialog;
   
        if ( wDialog != null ) {
            if ( wDialog.NavigatePrevious == null ) {
                throw new Exception("Wizard dialogs must have a Previous Button");
            }
            if ( wDialog.NavigateNext == null ) {
                throw new Exception("Wizard dialogs must have a Next Button");
            }
            if ( wDialog.NavigateFinish == null ) {
                throw new Exception("Wizard dialogs must have a Finish Button");
            }
            if ( wDialog.UIRoot == null ) {
                throw new Exception("Wizard dialogs must have a non null UI Root");
            }
        } else {
            throw new Exception("Wizard dialogs must support IWizardDialog");
        }

        wizardDialog = wDialog;
        wizardDialog.NavigatePrevious.Click += new EventHandler(Wizard_NavigatePrevious);
        wizardDialog.NavigateNext.Click += new EventHandler(Wizard_NavigateNext);
        wizardDialog.NavigateFinish.Click += new EventHandler(Wizard_NavigateFinish);
       
        // Last time we get a chance on the Form object
        dialog.Closing += new CancelEventHandler(Wizard_Closing);
    }
   
    public void AddPanel(Control pnl) {
        IWizardPanel wPanel = pnl as IWizardPanel;
        IWizardNavigation wNav = pnl as IWizardNavigation;

        if ( wPanel == null ) {
            throw new Exception("Wizard panels must support IWizardPanel");
        }

        if ( wNav != null ) {
            if ( nameMap.ContainsKey(wNav.NavigateName) ) {
                throw new Exception("Wizard Navigation Panels can't share names");
            }

            nameMap[wNav.NavigateName] = wNav;
        }

        wizardPanels.Add(wPanel);
    }
   
    public bool Complete {
        get {
            return complete;
        }
    }
   
    public ArrayList Panels {
        get {
            return wizardPanels;
        }
    }
   
    public void Wizard_Closing(object sender, CancelEventArgs e) {
        MessageBox.Show("You must complete the wizard in order to exit.");
        e.Cancel = true;
    }
   
    public void Wizard_NavigateFinish(object sender, EventArgs e) {
        // this could be fired anywhere in case you have an opt out
        // early button on any of your forms.
   
        wizardDialog.UIRoot.Controls.Clear();
        wizardDialog.Display = false;
        complete = true;
       
        ExitThread();
    }
   
    public void Wizard_NavigateNext(object sender, EventArgs e) {
        IWizardNavigation wizardNav = wizardPanels[wizardIndex] as IWizardNavigation;

        if ( wizardNav != null && wizardNav.SupportNamedNavigation ) {
            if ( nameMap.ContainsKey(wizardNav.NavigateNextTarget) ) {
                wizardIndex = wizardPanels.IndexOf(nameMap[wizardNav.NavigateNextTarget]);
            } else {
                // Since we can't find the next panel, we pop out
                // and finish.  Same as if we walked off the end
                // of the array before.
                wizardIndex = wizardPanels.Count;
            }
        } else {
            wizardIndex++;
        }

        if ( wizardIndex == wizardPanels.Count ) {
            Wizard_NavigateFinish(sender, e); // This shouldn't happen if your dialogs are correct
            return;
        }

        Control newPanel = wizardPanels[wizardIndex] as Control;

        if ( newPanel != null ) {
            InitPanel(newPanel);
        }
    }

    public void Wizard_NavigatePrevious(object sender, EventArgs e) {
        IWizardNavigation wizardNav = wizardPanels[wizardIndex] as IWizardNavigation;
        if ( wizardNav != null && wizardNav.SupportNamedNavigation ) {
            if ( nameMap.ContainsKey(wizardNav.NavigatePreviousTarget) ) {
                wizardIndex = wizardPanels.IndexOf(nameMap[wizardNav.NavigatePreviousTarget]);
            } else {
                // Can't go back from here I guess
                // This actually isn't bad, but the user should
                // have specified that no previous button be available.
                return;
            }
        } else {
            if ( wizardIndex > 0 ) {
                wizardIndex--;
            } else {
                return;
            }
        }

        Control newPanel = wizardPanels[wizardIndex] as Control;

        if ( newPanel != null ) {
            InitPanel(newPanel);
        }
    }
   
    public void StartWizard() {
        if ( wizardPanels.Count == 0 ) {
            throw new Exception("Must add panels to the wizard");
        }
        if ( wizardIndex != -1 && !complete ) {
            throw new Exception("Wizard has already been started");
        }
       
        complete = false;
        wizardIndex = 0;
        Control startPanel = wizardPanels[wizardIndex] as Control;
        if ( startPanel != null ) {
            InitPanel(startPanel);
        }
        wizardDialog.Display = true;
    }

    private void InitPanel(Control wizardPanel) {
        wizardPanel.Dock = DockStyle.Fill;
       
        wizardDialog.NavigatePrevious.Enabled   = ((IWizardPanel) wizardPanel).ShowNavigatePrevious;
        wizardDialog.NavigateNext.Enabled       = ((IWizardPanel) wizardPanel).ShowNavigateNext;
        wizardDialog.NavigateFinish.Enabled     = ((IWizardPanel) wizardPanel).ShowNavigateFinish;
       
        wizardDialog.UIRoot.Controls.Clear();
        wizardDialog.UIRoot.Controls.Add(wizardPanel);
    }
}

public class WizardPanel : DesignablePanel {
    protected string description = null;
    protected Label lblDescription = new Label();
   
    public WizardPanel(string description, bool prev, bool next, bool finish) {
        this.prev = prev;
        this.next = next;
        this.finish = finish;
        this.description = description;
       
        InitializeComponent();
    }
   
    private void InitializeComponent() {
        this.lblDescription.Dock = DockStyle.Fill;
        this.lblDescription.Text = this.description;
        this.Controls.Add(this.lblDescription);
    }
}

public class WizardPanelWithNavigation : DesignablePanelWithNavigation {
    protected string description = null;
    protected Label lblDescription = new Label();

    public WizardPanelWithNavigation(string name, string description, string prev, string next, bool finish) {
        this.name = name;
        this.nextPanel = next;
        this.prevPanel = prev;
        this.finish = finish;
        this.description = description;
       
        InitializeComponent();
    }
   
    private void InitializeComponent() {
        this.lblDescription.Dock = DockStyle.Fill;
        this.lblDescription.Text = this.description;
        this.Controls.Add(this.lblDescription);
    }
}

public class DesignablePanel : UserControl, IWizardPanel {
    protected bool prev = false;
    protected bool next = false;
    protected bool finish = false;

    public DesignablePanel() {
    }
   
    [Browsable(false)] public virtual bool ShowNavigatePrevious     { get { return this.prev;   } }
    [Browsable(false)] public virtual bool ShowNavigateNext         { get { return this.next;   } }
    [Browsable(false)] public virtual bool ShowNavigateFinish       { get { return this.finish; } }
   
    [Browsable(true)]
    [Category("Wizard")]
    [Description("Determines if the Previous button is activated when this panel is displayed")]
    public virtual bool NavigatePrevious {
        get {
            return this.prev;
        }
        set {
            this.prev = value;
        }
    }

    [Browsable(true)]
    [Category("Wizard")]
    [Description("Determines if the Next button is activated when this panel is displayed")]
    public virtual bool NavigateNext {
        get {
            return this.next;
        }
        set {
            this.next = value;
        }
    }
   
    [Browsable(true)]
    [Category("Wizard")]
    [Description("Determines if the Finish button is activated when this panel is displayed")]
    public virtual bool NavigateFinish {
        get {
            return this.finish;
        }
        set {
            this.finish = value;
        }
    }
}

public class DesignablePanelWithNavigation : DesignablePanel, IWizardNavigation {
    protected string name = null;
    protected string nextPanel = null;
    protected string prevPanel = null;
   
    public DesignablePanelWithNavigation() {
    }
   
    [Browsable(false)] public virtual bool SupportNamedNavigation   { get { return (this.nextPanel != null || this.prevPanel != null);    } }
    [Browsable(false)] public virtual string NavigateName           { get { return this.name;               } }
    [Browsable(false)] public virtual string NavigatePreviousTarget { get { return this.prevPanel;          } }
    [Browsable(false)] public virtual string NavigateNextTarget     { get { return this.nextPanel;          } }
    [Browsable(false)] public override bool ShowNavigatePrevious    { get { return this.prevPanel != null;  } }
    [Browsable(false)] public override bool ShowNavigateNext        { get { return this.nextPanel != null;  } }

    [Browsable(false)]
    public override bool NavigatePrevious {
        get {
            return ShowNavigatePrevious;
        }
        set { }
    }

    [Browsable(false)]
    public override bool NavigateNext {
        get {
            return ShowNavigateNext;
        }
        set { }
    }
   
    [Browsable(true)]
    [Category("Wizard")]
    [Description("Sets the name of this wizard panel for navigation purposes.  Each panel" +
                 "within a WizardController must have a unique name.")]
    public virtual string PanelName {
        get {
            return this.name;
        }
        set {
            this.name = value;
        }
    }

    [Browsable(true)]
    [Category("Wizard")]
    [Description("Sets the name of the next panel in the series.  This property, if set, enables" +
                 "the SupportNamedNavigation feature and the display of the Next button within" +
                 "a WizardDialog")]
    public virtual string NextTargetName {
        get {
            return this.nextPanel;
        }
        set {
            this.nextPanel = value;
        }
    }

    [Browsable(true)]
    [Category("Wizard")]
    [Description("Sets the name of the previous panel in the series.  This property, if set, enables" +
                 "the SupportNamedNavigation feature and the display of the Previous button within" +
                 "a WizardDialog")]
    public virtual string PreviousTargetName {
        get {
            return this.prevPanel;
        }
        set {
            this.prevPanel = value;
        }
    }
}

public class WizardTestDialog : Form, IWizardDialog {
    private Panel topPanel = new Panel();
    private Panel bottomPanel = new Panel();

    private Button previousButton;
    private Button nextButton;
    private Button finishButton;
   
    private string title;

    public WizardTestDialog(string title) {
        this.title = title;

        InitializeComponent();
    }
   
    public Control NavigatePrevious {
        get {
            return previousButton;
        }
    }

    public Control NavigateNext {
        get {
            return nextButton;
        }
    }

    public Control NavigateFinish {
        get {
            return finishButton;
        }
    }
   
    public Control UIRoot {
        get {
            return topPanel;
        }
    }
   
    public bool Display {
        get {
            return this.Visible;
        }
        set {
            this.Visible = value;
        }
    }
   
    private void InitializeComponent() {
        // Create our large containers
        // Top for wizard
        // Bottom for navigation
        this.bottomPanel.Height = 30;
        this.bottomPanel.Dock = DockStyle.Bottom;
       
        this.topPanel.Dock = DockStyle.Fill;
       
        this.Text = title;
        this.Controls.AddRange(new Control[] { topPanel, bottomPanel });
       
        this.finishButton = new Button();
        this.finishButton.Text = "Finish";
        this.finishButton.Left = this.bottomPanel.Width - (this.finishButton.Width + 10);
        this.finishButton.Anchor = AnchorStyles.Right;

        this.nextButton = new Button();
        this.nextButton.Text = "Next";
        this.nextButton.Left = this.finishButton.Left - (this.nextButton.Width + 5);
        this.nextButton.Anchor = AnchorStyles.Right;

        this.previousButton = new Button();
        this.previousButton.Text = "Previous";
        this.previousButton.Left = this.nextButton.Left - (this.previousButton.Width + 5);
        this.previousButton.Anchor = AnchorStyles.Right;
       
        this.bottomPanel.Controls.AddRange(new Control[] { previousButton, nextButton, finishButton });
    }
}

public class WizardTester {
    [STAThread()]
    private static void Main(string[] args) {
        WizardController controller = new WizardController();
        controller.SetDialog(new WizardTestDialog("What do you want?"));
        controller.AddPanel(new WizardPanel("foo 1", false, true, false));
        controller.AddPanel(new WizardPanel("foo 2", true, true, false));
        controller.AddPanel(new WizardPanel("foo 3", true, true, false));
        controller.AddPanel(new WizardPanel("foo 4", true, true, true));
        controller.AddPanel(new WizardPanel("foo 5", true, false, true));

        controller.StartWizard();
        Application.Run(controller);

        controller = new WizardController();
        controller.SetDialog(new WizardTestDialog("What do you want?"));
        controller.AddPanel(new WizardPanelWithNavigation("Panel 1", "Welcome Screen!",     null, "Panel 3", false));
        controller.AddPanel(new WizardPanelWithNavigation("Panel 2", "License Agreement!",  "Panel 3", "Panel 4", false));
        controller.AddPanel(new WizardPanelWithNavigation("Panel 3", "Install Options",     "Panel 1", "Panel 2", false));
        controller.AddPanel(new WizardPanelWithNavigation("Panel 4", "Confirmation Screen", "Panel 2", "Panel 5", false));
        controller.AddPanel(new WizardPanelWithNavigation("Panel 5", "Install Now!",        null, null, true));

        controller.StartWizard();
        Application.Run(controller);
    }
}

Published Friday, April 23, 2004 7:44 PM by Justin Rogers

Comments

No Comments

Leave a Comment

(required) 
(required) 
(optional)
(required)