Code-Only: Winforms Wizard Series Article 5 (Managed C++)

#using <mscorlib.dll>
#using <System.dll>
#using <System.Windows.Forms.dll>

using namespace System;
using namespace System::Collections;
using namespace System::ComponentModel;
using namespace System::Threading;
using namespace System::Windows::Forms;

__gc __interface IWizardDialog {
    __property Control* get_NavigatePrevious();
    __property Control* get_NavigateNext();
    __property Control* get_NavigateFinish();
    __property Control* get_UIRoot();
    __property Boolean get_Display();
    __property void set_Display(Boolean value);
};

__gc __interface IWizardPanel {
    __property Boolean get_ShowNavigatePrevious();
    __property Boolean get_ShowNavigateNext();
    __property Boolean get_ShowNavigateFinish();
};

__gc __interface IWizardNavigation {
    __property Boolean get_SupportNamedNavigation();
    __property String* get_NavigateName();
    __property String* get_NavigatePreviousTarget();
    __property String* get_NavigateNextTarget();
};

__gc class WizardController : public ApplicationContext {

    private:
    Boolean complete;
    int wizardIndex;
    IWizardDialog* wizardDialog;
    ArrayList* wizardPanels;
    Hashtable* nameMap;

    public:
    WizardController() {
        complete = false;
        wizardIndex = -1;
        wizardDialog = 0;
        wizardPanels = new ArrayList();
        nameMap = new Hashtable();
    }

    void SetDialog(Form* dialog) {
        wizardDialog = dynamic_cast<IWizardDialog*>(dialog);

        if ( wizardDialog == 0 ) {
            throw new Exception("Wizard dialogs must support IWizardDialog");

        }

        if ( wizardDialog->NavigatePrevious == 0 ) {
            throw new Exception("Wizard dialogs must have a Previous Button");
        }
        if ( wizardDialog->NavigateNext == 0 ) {
            throw new Exception("Wizard dialogs must have a Next Button");
        }
        if ( wizardDialog->NavigateFinish == 0 ) {
            throw new Exception("Wizard dialogs must have a Finish Button");
        }
        if ( wizardDialog->UIRoot == 0 ) {
            throw new Exception("Wizard dialogs must have a non null UI Root");
        }

        wizardDialog->NavigatePrevious->Click += new EventHandler(this, &WizardController::Wizard_NavigatePrevious);
        wizardDialog->NavigateNext->Click += new EventHandler(this, &WizardController::Wizard_NavigateNext);
        wizardDialog->NavigateFinish->Click += new EventHandler(this, &WizardController::Wizard_NavigateFinish);

        // Last time we get a chance on the Form object
        dialog->Closing += new CancelEventHandler(this, &WizardController::Wizard_Closing);
    }

    void AddPanel(Control* pnl) {
        IWizardPanel* wPanel = dynamic_cast<IWizardPanel*>(pnl);
        IWizardNavigation* wNav = dynamic_cast<IWizardNavigation*>(pnl);

        if ( wPanel == 0 ) { throw new Exception("Wizard panels must support IWizardPanel"); }
        if ( wNav != 0 ) {
            if ( nameMap->ContainsKey(wNav->NavigateName) ) {
                throw new Exception("Wizard Navigation Panels can't share names");
            }

            nameMap->set_Item(wNav->NavigateName, wNav);
        }

        wizardPanels->Add(wPanel);
    }

    __property Boolean get_Complete() {
        return complete;
    }

    __property ArrayList* get_Panels() {
        return wizardPanels;
    }

    private:
    void Wizard_Closing(Object* sender, CancelEventArgs* e) {
        // Show(L"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();
    }

    void Wizard_NavigateNext(Object* sender, EventArgs* e) {
        IWizardNavigation* wizardNav = dynamic_cast<IWizardNavigation*>(wizardPanels->get_Item(wizardIndex));

        if ( wizardNav != 0 && wizardNav->SupportNamedNavigation ) {
            if ( nameMap->ContainsKey(wizardNav->NavigateNextTarget) ) {
                wizardIndex = wizardPanels->IndexOf(nameMap->get_Item(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 = dynamic_cast<Control*>(wizardPanels->get_Item(wizardIndex));

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

    void Wizard_NavigatePrevious(Object* sender, EventArgs* e) {
        IWizardNavigation* wizardNav = dynamic_cast<IWizardNavigation*>(wizardPanels->get_Item(wizardIndex));
        if ( wizardNav != 0 && wizardNav->SupportNamedNavigation ) {
            if ( nameMap->ContainsKey(wizardNav->NavigatePreviousTarget) ) {
                wizardIndex = wizardPanels->IndexOf(nameMap->get_Item(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 = dynamic_cast<Control*>(wizardPanels->get_Item(wizardIndex));

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

    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 = dynamic_cast<Control*>(wizardPanels->get_Item(wizardIndex));
        if ( startPanel != 0 ) {
            InitPanel(startPanel);
        }
        wizardDialog->Display = true;
    }

    private:
    void InitPanel(Control* wizardPanel) {
        wizardPanel->Dock = DockStyle::Fill;

        IWizardPanel* iwp = dynamic_cast<IWizardPanel*>(wizardPanel);
        if ( iwp != 0 ) {
            wizardDialog->NavigatePrevious->Enabled = iwp->ShowNavigatePrevious;
            wizardDialog->NavigateNext->Enabled     = iwp->ShowNavigateNext;
            wizardDialog->NavigateFinish->Enabled   = iwp->ShowNavigateFinish;
        }

        wizardDialog->UIRoot->Controls->Clear();
        wizardDialog->UIRoot->Controls->Add(wizardPanel);
    }
};

__gc class DesignablePanel : public UserControl, public IWizardPanel {
    protected:
    Boolean prev;
    Boolean next;
    Boolean finish;

    public:
    DesignablePanel() {
        prev = false;
        next = false;
        finish = false;
    }

    [Browsable(false)] __property virtual Boolean get_ShowNavigatePrevious() { return this->prev; }
    [Browsable(false)] __property virtual Boolean get_ShowNavigateNext() { return this->next; }
    [Browsable(false)] __property virtual Boolean get_ShowNavigateFinish() { return this->finish; }

    [Browsable(true)]
    [Category("Wizard")]
    [Description("Determines if the Previous button is activated when this panel is displayed")]
    __property virtual Boolean get_NavigatePrevious() { return this->prev; }
    __property virtual void set_NavigatePrevious(Boolean value) { this->prev = value; }

    [Browsable(true)]
    [Category("Wizard")]
    [Description("Determines if the Next button is activated when this panel is displayed")]
    __property virtual Boolean get_NavigateNext() { return this->next; }
    __property virtual void set_NavigateNext(Boolean value) { this->next = value; }

    [Browsable(true)]
    [Category("Wizard")]
    [Description("Determines if the Finish button is activated when this panel is displayed")]
    __property virtual Boolean get_NavigateFinish() { return this->finish; }
    __property virtual void set_NavigateFinish(Boolean value) { this->finish = value; }
};

__gc class WizardPanel : public DesignablePanel {
    protected:
    String* description;
    Label* lblDescription;

    public:
    WizardPanel(String* description, Boolean prev, Boolean next, Boolean finish) {
        this->prev = prev;
        this->next = next;
        this->finish = finish;
        this->description = description;
        this->lblDescription = new Label();

        InitializeComponent();
    }

    private:
    void InitializeComponent() {
        this->lblDescription->Dock = DockStyle::Fill;
        this->lblDescription->Text = this->description;
        this->Controls->Add(this->lblDescription);
    }
};

__gc class DesignablePanelWithNavigation : public DesignablePanel, public IWizardNavigation {
    protected:
    String* name;
    String* nextPanel;
    String* prevPanel;

    public:
    DesignablePanelWithNavigation() {
    }

    [Browsable(false)] __property virtual Boolean get_SupportNamedNavigation()  { return (this->nextPanel != 0 || this->prevPanel != 0); }
    [Browsable(false)] __property virtual String* get_NavigateName()            { return this->name; }
    [Browsable(false)] __property virtual String* get_NavigatePreviousTarget()  { return this->prevPanel; }
    [Browsable(false)] __property virtual String* get_NavigateNextTarget()      { return this->nextPanel; }
    [Browsable(false)] __property virtual Boolean get_ShowNavigatePrevious()    { return (this->prevPanel != 0); }
    [Browsable(false)] __property virtual Boolean get_ShowNavigateNext()        { return (this->nextPanel != 0); }

    [Browsable(false)]
    __property virtual Boolean get_NavigatePrevious() { return this->get_ShowNavigatePrevious(); }
    __property virtual void set_NavigatePrevious(Boolean value) { }

    [Browsable(false)]
    __property virtual Boolean get_NavigateNext() { return this->get_ShowNavigateNext(); }
    __property virtual void set_NavigateNext(Boolean value) { }

    [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.")]
    __property virtual String* get_PanelName() { return this->name; }
    __property virtual void set_PanelName(String* value) { 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")]
    __property virtual String* get_NextTargetName() { return this->nextPanel; }
    __property virtual void set_NextTargetName(String* value) { 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")]
    __property virtual String* get_PreviousTargetName() { return this->prevPanel; }
    __property virtual void set_PreviousTargetName(String* value) { this->prevPanel = value; }
};

__gc class WizardPanelWithNavigation : public DesignablePanelWithNavigation {
    protected:
    String* description;
    Label* lblDescription;

    public:
    WizardPanelWithNavigation(String* name, String* description, String* prev, String* next, Boolean finish) {
        this->description = 0;
        this->lblDescription = new Label();
        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);
    }
};

__gc class DesignableWizardDialog : public Form, public IWizardDialog {
    protected:
    Control* previousButton;
    Control* nextButton;
    Control* finishButton;
    Control* uiRoot;

    public:
    DesignableWizardDialog() { }

    [Browsable(false)] __property virtual Control* get_NavigatePrevious()  { return previousButton; }
    [Browsable(false)] __property virtual Control* get_NavigateNext()      { return nextButton; }
    [Browsable(false)] __property virtual Control* get_NavigateFinish()    { return finishButton; }
    [Browsable(false)] __property virtual Control* get_UIRoot()            { return uiRoot; }
    [Browsable(false)]
    __property virtual Boolean get_Display() { return this->Visible; }
    __property virtual void set_Display(Boolean value) { this->Visible = value; }

    [Browsable(true)]
    [Category("Wizard")]
    [Description("Set the control used for previous panel navigation")]
    __property virtual Control* get_NavigatePreviousControl() { return this->previousButton; }
    __property virtual void set_NavigatePreviousControl(Control* value) { this->previousButton = value; }

    [Browsable(true)]
    [Category("Wizard")]
    [Description("Set the control used for next panel navigation")]
    __property virtual Control* get_NavigateNextControl() { return this->nextButton; }
    __property virtual void set_NavigateNextControl(Control* value) { this->nextButton = value; }

    [Browsable(true)]
    [Category("Wizard")]
    [Description("Set the control used for finish navigation")]
    __property virtual Control* get_NavigateFinishControl() { return this->finishButton; }
    __property virtual void set_NavigateFinishControl(Control* value) { this->finishButton = value; }

    [Browsable(true)]
    [Category("Wizard")]
    [Description("Sets the control where all panels will be displayed")]
    __property Control* get_UIRootControl() { return this->uiRoot; }
    __property void set_UIRootControl(Control* value) { this->uiRoot = value; }
};

__gc class WizardTestDialog : public DesignableWizardDialog {
    private:
    Panel* bottomPanel;
    String* title;

    public:
    WizardTestDialog(String* title) {
        this->title = title;
        this->bottomPanel = new Panel;

        InitializeComponent();
    }

    private:
    void InitializeComponent() {
        // Create our large containers
        // Top for wizard
        // Bottom for navigation
        this->bottomPanel = new Panel();
        this->bottomPanel->Height = 30;
        this->bottomPanel->Dock = DockStyle::Bottom;

        this->uiRoot = new Panel();
        this->uiRoot->Dock = DockStyle::Fill;

        this->Text = title;
        Control* layer1[] = { this->uiRoot, this->bottomPanel };
        this->Controls->AddRange(layer1);

        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;

        Control* layer2[] = { previousButton, nextButton, finishButton };
        this->bottomPanel->Controls->AddRange(layer2);
    }
};

__gc class WizardTester {
    public:
    [STAThread]
    static void Main() {
        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!",     0, "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!",        0, 0, true));

        controller->StartWizard();
        Application::Run(controller);
    }
};

void main() {
    WizardTester::Main();
}

Published Monday, April 26, 2004 5:06 AM by Justin Rogers

Comments

Monday, April 26, 2004 8:27 AM by Justin Shen

# re: Code-Only: Winforms Wizard Series Article 5 (Managed C++)

the syntax of managed C++ is being refined, the new syntax looks more elegant.
Monday, April 26, 2004 8:27 AM by Justin Shen

# re: Code-Only: Winforms Wizard Series Article 5 (Managed C++)

the syntax of managed C++ is being refined, the new syntax looks more elegant.
Monday, April 26, 2004 8:37 AM by Justin Rogers

# re: Code-Only: Winforms Wizard Series Article 5 (Managed C++)

Don't know if I'd call it more elegant. I kind of like the new property syntax, but for the managed pointer changes (namely ^), I can't say that I like that at all. I could have made the above code much more *elegant* as well, but I chose for the quick port, and not necessarily the most elegant port.

Leave a Comment

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