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();
}