Code-Only: Winforms Wizard Series Article 2 (VB .NET)
Imports System
Imports System.Collections
Imports System.ComponentModel
Imports System.Threading
Imports System.Windows.Forms
Public Interface IWizardDialog
ReadOnly Property NavigatePrevious() As Control
ReadOnly Property NavigateNext() As Control
ReadOnly Property NavigateFinish() As Control
ReadOnly Property UIRoot() As Control
Property Display() As Boolean
End Interface
Public Interface IWizardPanel
ReadOnly Property ShowNavigatePrevious() As Boolean
ReadOnly Property ShowNavigateNext() As Boolean
ReadOnly Property ShowNavigateFinish() As Boolean
End Interface
Public Class WizardController : Inherits ApplicationContext
Private _complete As Boolean = False
Private _wizardIndex As Integer = -1
Private _wizardDialog As IWizardDialog = Nothing
Private _wizardPanels As New ArrayList()
Public Sub New()
End Sub
Public Sub SetDialog(dialog As Form)
Try
_wizardDialog = DirectCast(dialog, IWizardDialog)
Catch
_wizardDialog = Nothing
End Try
If _wizardDialog Is Nothing
Throw New Exception("Wizard dialogs must support IWizardDialog")
Else
If _wizardDialog.NavigatePrevious Is Nothing Then Throw New Exception("Wizard dialogs must have a Previous Button")
If _wizardDialog.NavigateNext Is Nothing Then Throw New Exception("Wizard dialogs must have a Next Button")
If _wizardDialog.NavigateFinish Is Nothing Then Throw New Exception("Wizard dialogs must have a Finish Button")
If _wizardDialog.UIRoot Is Nothing Then Throw New Exception("Wizard dialogs must have a non null UI Root")
End If
AddHandler _wizardDialog.NavigatePrevious.Click, AddressOf Me.Wizard_NavigatePrevious
AddHandler _wizardDialog.NavigateNext.Click, AddressOf Me.Wizard_NavigateNext
AddHandler _wizardDialog.NavigateFinish.Click, AddressOf Me.Wizard_NavigateFinish
AddHandler dialog.Closing, AddressOf Me.Wizard_Closing
End Sub
Public Sub AddPanel(pnl As Panel)
Dim vPanel As IWizardPanel = Nothing
Try
vPanel = DirectCast(pnl, IWizardPanel)
Catch
vPanel = Nothing
End Try
If vPanel Is Nothing Then Throw New Exception("Wizard panels must support IWizardPanel")
_wizardPanels.Add(vPanel)
End Sub
Public ReadOnly Property Complete() As Boolean
Get
Return _complete
End Get
End Property
Public ReadOnly Property Panels() As ArrayList
Get
Return _wizardPanels
End Get
End Property
Public Sub Wizard_Closing(Sender As Object, E As CancelEventArgs)
MessageBox.Show("You must complete the wizard in order to exit.")
e.Cancel = True
End Sub
Public Sub Wizard_NavigateFinish(Sender As Object, E As EventArgs)
' 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()
End Sub
Public Sub Wizard_NavigateNext(Sender As Object, E As EventArgs)
_wizardIndex += 1
If _wizardIndex = _wizardPanels.Count
Wizard_NavigateFinish(Sender, E) ' This shouldn't happen if your dialogs are correct
Return
End If
Try
Dim newPanel As Panel = DirectCast(_wizardPanels(_wizardIndex), Panel)
InitPanel(newPanel)
Catch
' Do Nothing
End Try
End Sub
Public Sub Wizard_NavigatePrevious(Sender As Object, E As EventArgs)
If _wizardIndex > 0
Try
Dim newPanel As Panel = DirectCast(_wizardPanels(_wizardIndex - 1), Panel)
InitPanel(newPanel)
_wizardIndex -= 1
Catch
' Do Nothing
End Try
End If
End Sub
Public Sub StartWizard()
If _wizardPanels.Count = 0 Then Throw New Exception("Must add panels to the wizard")
If _wizardIndex <> -1 AndAlso Not _complete Then Throw New Exception("Wizard has already been started")
_complete = False
_wizardIndex = 0
Try
Dim startPanel As Panel = DirectCast(_wizardPanels(_wizardIndex), Panel)
InitPanel(startPanel)
Catch
' Do Nothing
End Try
_wizardDialog.Display = True
End Sub
Public Sub InitPanel(wizardPanel As Panel)
wizardPanel.Dock = DockStyle.Fill
Try
Dim iwp As IWizardPanel = DirectCast(wizardPanel, IWizardPanel)
_wizardDialog.NavigatePrevious.Enabled = iwp.ShowNavigatePrevious
_wizardDialog.NavigateNext.Enabled = iwp.ShowNavigateNext
_wizardDialog.NavigateFinish.Enabled = iwp.ShowNavigateFinish
Catch
' Do Nothing
End Try
_wizardDialog.UIRoot.Controls.Clear()
_wizardDialog.UIRoot.Controls.Add(wizardPanel)
End Sub
End Class
Public Class WizardPanel : Inherits Panel : Implements IWizardPanel
Private _prev As Boolean = False
Private _next As Boolean = False
Private _finish As Boolean = False
Private _description As New Label()
Public Sub New(description As String, showprev As Boolean, shownext As Boolean, showfinish As Boolean)
_prev = showprev
_next = shownext
_finish = showfinish
_description.Text = description
InitializeComponent()
End Sub
Private Sub InitializeComponent()
Me._description.Dock = DockStyle.Fill
Me.Controls.Add(me._description)
End Sub
Public ReadOnly Property ShowNavigatePrevious() As Boolean Implements IWizardPanel.ShowNavigatePrevious
Get
Return _prev
End Get
End Property
Public ReadOnly Property ShowNavigateNext() As Boolean Implements IWizardPanel.ShowNavigateNext
Get
Return _next
End Get
End Property
Public ReadOnly Property ShowNavigateFinish() As Boolean Implements IWizardPanel.ShowNavigateFinish
Get
Return _finish
End Get
End Property
End Class
Public Class WizardTestDialog : Inherits Form : Implements IWizardDialog
Private _topPanel As New Panel()
Private _bottomPanel As New Panel()
Private _previousButton As Button
Private _nextButton As Button
Private _finishButton As Button
Private _title As String
Public Sub New(title As String)
_title = title
InitializeComponent()
End Sub
Public ReadOnly Property NavigatePrevious() As Control Implements IWizardDialog.NavigatePrevious
Get
Return _previousButton
End Get
End Property
Public ReadOnly Property NavigateNext() As Control Implements IWizardDialog.NavigateNext
Get
Return _nextButton
End Get
End Property
Public ReadOnly Property NavigateFinish() As Control Implements IWizardDialog.NavigateFinish
Get
Return _finishButton
End Get
End Property
Public ReadOnly Property UIRoot() As Control Implements IWizardDialog.UIRoot
Get
Return _topPanel
End Get
End Property
Public Property Display() As Boolean Implements IWizardDialog.Display
Get
Return Me.Visible
End Get
Set(Value As Boolean)
Me.Visible = Value
End Set
End Property
Private Sub InitializeComponent()
' Create our large containers
' Top for wizard
' Bottom for navigation
_bottomPanel.Height = 30
_bottomPanel.Dock = DockStyle.Bottom
_topPanel.Dock = DockStyle.Fill
Me.Text = _title
Me.Controls.AddRange(New Control() { _topPanel, _bottomPanel })
_finishButton = New Button()
_finishButton.Text = "Finish"
_finishButton.Left = _bottomPanel.Width - (_finishButton.Width + 10)
_finishButton.Anchor = AnchorStyles.Right
_nextButton = New Button()
_nextButton.Text = "Next"
_nextButton.Left = _finishButton.Left - (_nextButton.Width + 5)
_nextButton.Anchor = AnchorStyles.Right
_previousButton = New Button()
_previousButton.Text = "Previous"
_previousButton.Left = _nextButton.Left - (_previousButton.Width + 5)
_previousButton.Anchor = AnchorStyles.Right
_bottomPanel.Controls.AddRange(New Control() { _previousButton, _nextButton, _finishButton })
End Sub
End Class
Public Class WizardTester
<STAThread()> _
Shared Sub Main()
Dim controller As 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)
End Sub
End Class