How to convert TabControl into wizard style in .NE

2019-08-17 23:26发布

问题:

I want to have my form in a wizard style and so I used TabControl to have the pages of the Wizard as my TabPages. There were small issues to be corrected, such as, making the tabs being displayed in runtime. I inherited the TabControl and I added a property called "TabsVisible" and corrected it. It worked fine. (See : http://dotnetrix.co.uk/tabcontrol.htm - Add a HideTabs property to turn on/off the Tabitems)

But there are other small issues like : 1. When Ctrl + Tab is pressed the tabs get changed. This is disabled by overriding the OnKeyDown method 2. When the active cursor is in the tab list, and if Arrow keys are pressed, the current tab page gets changed. How can I disable this??

So my question is - How can disable Arrow keys in tabControl so that the tab page doesn't get changed?

回答1:

In other words: you don't need the tab control. Why don't you just use Panels to contain your GUI for the different Wizard steps, and buttons or something else as the Wizard steps themselves, and save yourself all that trouble? That way you'll be free of all this, AND will be able to style it however you see fit - much simpler and much more flexible.



回答2:

To disable the arrow key from changing the page of your TabControl, you can handle the KeyDown event, and do something like this:

private void tabControl_KeyDown(object sender, KeyEventArgs e)
{
    bool isArrowKey = e.KeyCode == Keys.Up || e.KeyCode == Keys.Down ||
                      e.KeyCode == Keys.Left || e.KeyCode == Keys.Right;

    e.Handled = isArrowKey;
}

So basically you mark the event as handled if the pressed key is an arrow key.



回答3:

I created a WizardControl framework. It uses a wizardFormBase. You load the form with a UserControl collection. The rest happens auto-magically. This was based on the following series ...

http://weblogs.asp.net/justin_rogers/articles/60155.aspx

Article 1 is pretty stupid...but he fixes it by article 2.

I did not do it exactly like this. Here are my bits for this. You will have to remove the telerik controls references and replace with actual winforms control references.

You setup the UIRoot which is basically the panel control (or any other container control) to load the pages into. The Pages are USerControls but you could change that to host any type of control.

Imports System.Windows
Imports System.ComponentModel
Imports System.Windows.Forms


Public Interface IWizardDialog
    ReadOnly Property NavigatePrevious() As Control
    ReadOnly Property NavigateNext() As Control
    ReadOnly Property NavigateFinish() As Control
    ReadOnly Property NavigateCancel() As Control
    ReadOnly Property UIRoot() As Control
    Property Display() As Boolean
End Interface
Public Interface IWizardUserControl
    ReadOnly Property ShowNavigatePrevious() As Boolean
    ReadOnly Property ShowNavigateNext() As Boolean
    ReadOnly Property ShowNavigateFinish() As Boolean
    ReadOnly Property ShowNavigateCancel() As Boolean
    ReadOnly Property Description() As String
    ReadOnly Property StepCaption() As String
End Interface

Public Class WizardController
    Private _complete As Boolean = False
    Private _wizardIndex As Integer = -1
    Private _wizardDialog As IWizardDialog
    Private _wizardUserControls As New ArrayList()

    Private _allowClose As Boolean = False

    Public Sub SetDialog(ByVal dialog As Form)
        _wizardDialog = TryCast(dialog, IWizardDialog)
        If _wizardDialog Is Nothing Then
            Throw New Exception("Wizard must support IWizardDialog interface")
        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.NavigateCancel Is Nothing Then Throw New Exception("Wizard Dialogs must have a cancel button.")
            If _wizardDialog.UIRoot Is Nothing Then Throw New Exception("Wizard Dialog must have a  non null UI Container.")
        End If

        AddHandler _wizardDialog.NavigatePrevious.Click, AddressOf Wizard_NavigatePrevious
        AddHandler _wizardDialog.NavigateNext.Click, AddressOf Wizard_NavigateNext
        AddHandler _wizardDialog.NavigateFinish.Click, AddressOf Wizard_NavigateFinish
        AddHandler _wizardDialog.NavigateCancel.Click, AddressOf Wizard_Cancel
        AddHandler dialog.Closing, AddressOf Me.Wizard_Closing

    End Sub

    Public Sub AddUserControl(ByVal ctrl As UserControl)
        Dim vUserControl As IWizardUserControl = TryCast(ctrl, IWizardUserControl)
        If vUserControl Is Nothing Then Throw New Exception("UserControl must implement IWizardUserControl interface.")
        _wizardUserControls.Add(ctrl)
    End Sub

    Public Sub StartWizard()
        If _wizardUserControls.Count = 0 Then
            Throw New Exception("Must add dialogs to the wizard")
        End If
        If _wizardIndex <> -1 AndAlso Not _complete Then
            Throw New Exception("Wizard has already been started")
        End If

        _complete = False

        _wizardIndex = 0

        Try
            Dim startControl As UserControl = DirectCast(_wizardUserControls(_wizardIndex), UserControl)
            InitUserControl(startControl)
        Catch ex As Exception
            'do nothing
        End Try
        _wizardDialog.Display = True

    End Sub
    Public Sub InitUserControl(ByVal wizardUserControl As UserControl)

        wizardUserControl.Dock = DockStyle.Fill

        Try
            Dim iwp As IWizardUserControl = DirectCast(wizardUserControl, IWizardUserControl)

            _wizardDialog.NavigatePrevious.Enabled = iwp.ShowNavigatePrevious
            _wizardDialog.NavigateNext.Enabled = iwp.ShowNavigateNext
            _wizardDialog.NavigateFinish.Enabled = iwp.ShowNavigateFinish
            _wizardDialog.NavigateCancel.Enabled = iwp.ShowNavigateCancel
            TryCast(_wizardDialog, Form).Text = iwp.StepCaption

        Catch
            ' Do Nothing
        End Try

        _wizardDialog.UIRoot.Controls.Clear()
        _wizardDialog.UIRoot.Controls.Add(wizardUserControl)

    End Sub

    Public Sub Wizard_Closing(ByVal Sender As Object, ByVal E As CancelEventArgs)
        If _allowClose = False Then
            MessageBox.Show("You must complete the wizard in order to exit.")
            E.Cancel = True
        End If
    End Sub

    Private Sub CloseWizard(Optional ByVal diagResult As DialogResult = DialogResult.Cancel)
        'allows closing of wizard
        _wizardDialog.UIRoot.Controls.Clear()
        _complete = True
        _allowClose = True
        TryCast(_wizardDialog, Form).DialogResult = diagResult
    End Sub

    Public Sub Wizard_NavigateNext(ByVal sender As Object, ByVal e As EventArgs)
        _wizardIndex += 1

        If _wizardIndex = _wizardUserControls.Count Then
            CloseWizard()
            ' This shouldn't happen if your dialogs are correct
            Exit Sub
        End If

        Try
            Dim nextControl As UserControl = DirectCast(_wizardUserControls(_wizardIndex), UserControl)
            InitUserControl(nextControl)
        Catch ex As Exception
            'do nothing
        End Try

    End Sub

    Public Sub Wizard_NavigatePrevious(ByVal sender As Object, ByVal e As EventArgs)
        If _wizardIndex > 0 Then
            Try
                Dim newControl As UserControl = DirectCast(_wizardUserControls(_wizardIndex - 1), UserControl)
                InitUserControl(newControl)
                _wizardIndex -= 1
            Catch
                ' Do Nothing
            End Try
        End If
    End Sub

    Public Sub Wizard_NavigateFinish(ByVal sender As Object, ByVal e As EventArgs)
        ' this could be fired anywhere in case you have an opt out
        ' early button on any of your forms.

        CloseWizard(DialogResult.OK)

    End Sub

    Public Sub Wizard_Cancel(ByVal sender As Object, ByVal e As EventArgs)
        'put cancel code here
        'if needed put validation to see if cancel is allowed.  But generally you will allow cancel.
        CloseWizard()
    End Sub

    Public ReadOnly Property Complete() As Boolean
        Get
            Return _complete
        End Get
    End Property

    Public ReadOnly Property UserControls() As ArrayList
        Get
            Return _wizardUserControls
        End Get
    End Property
    Public ReadOnly Property WizardDialog() As IWizardDialog
        Get
            Return _wizardDialog
        End Get
    End Property


End Class

WizardFormBase

Imports System.Windows.Forms
Imports Telerik.WinControls.UI



''' <summary>
''' WizardFormBase
''' </summary>
''' <remarks></remarks>
Public Class WizardFormBase
    Implements IWizardDialog

    Private _title As String = String.Empty

    ''' <summary>
    ''' Initializes a new instance of the WizardFormBase class.
    ''' </summary>
    Public Sub New(ByVal title As String)

        _title = title
        InitializeComponent()

    End Sub

    Public ReadOnly Property NavigateCancel() As System.Windows.Forms.Control Implements IWizardDialog.NavigateCancel
        Get
            Return RadBtn_Cancel
        End Get
    End Property

    Public ReadOnly Property NavigateFinish() As System.Windows.Forms.Control Implements IWizardDialog.NavigateFinish
        Get
            Return RadBtn_Finish
        End Get
    End Property

    Public ReadOnly Property NavigateNext() As System.Windows.Forms.Control Implements IWizardDialog.NavigateNext
        Get
            Return RadBtn_Next
        End Get
    End Property

    Public ReadOnly Property NavigatePrevious() As System.Windows.Forms.Control Implements IWizardDialog.NavigatePrevious
        Get
            Return RadBtn_Previous
        End Get
    End Property

    Public Property Display() As Boolean Implements IWizardDialog.Display
        Get
            Return Me.Visible
        End Get
        Set(ByVal value As Boolean)
            If value = True Then Me.ShowDialog()
        End Set
    End Property

    Public ReadOnly Property UIRoot() As System.Windows.Forms.Control Implements IWizardDialog.UIRoot
        Get
            Return Me.mainPanel
        End Get
    End Property
End Class

WizardFormBase designer file

<Global.Microsoft.VisualBasic.CompilerServices.DesignerGenerated()> _
Partial Class WizardFormBase
    Inherits FormBase

    'Form overrides dispose to clean up the component list.
    <System.Diagnostics.DebuggerNonUserCode()> _
    Protected Overrides Sub Dispose(ByVal disposing As Boolean)
        If disposing AndAlso components IsNot Nothing Then
            components.Dispose()
        End If
        MyBase.Dispose(disposing)
    End Sub

    'Required by the Windows Form Designer
    Private components As System.ComponentModel.IContainer

    'NOTE: The following procedure is required by the Windows Form Designer
    'It can be modified using the Windows Form Designer.  
    'Do not modify it using the code editor.
    <System.Diagnostics.DebuggerStepThrough()> _
    Private Sub InitializeComponent()
        Me.RadBtn_Next = New Telerik.WinControls.UI.RadButton
        Me.RadBtn_Cancel = New Telerik.WinControls.UI.RadButton
        Me.RadBtn_Finish = New Telerik.WinControls.UI.RadButton
        Me.RadBtn_Previous = New Telerik.WinControls.UI.RadButton
        Me.pnlSetupWizardFormButtom = New Telerik.WinControls.UI.RadPanel
        Me.mainPanel = New System.Windows.Forms.Panel
        CType(Me.RadBtn_Next, System.ComponentModel.ISupportInitialize).BeginInit()
        CType(Me.RadBtn_Cancel, System.ComponentModel.ISupportInitialize).BeginInit()
        CType(Me.RadBtn_Finish, System.ComponentModel.ISupportInitialize).BeginInit()
        CType(Me.RadBtn_Previous, System.ComponentModel.ISupportInitialize).BeginInit()
        CType(Me.pnlSetupWizardFormButtom, System.ComponentModel.ISupportInitialize).BeginInit()
        Me.pnlSetupWizardFormButtom.SuspendLayout()
        Me.SuspendLayout()
        '
        'RadBtn_Next
        '
        Me.RadBtn_Next.Location = New System.Drawing.Point(436, 13)
        Me.RadBtn_Next.Name = "RadBtn_Next"
        Me.RadBtn_Next.Size = New System.Drawing.Size(75, 23)
        Me.RadBtn_Next.TabIndex = 0
        Me.RadBtn_Next.Text = "Next >>"
        Me.RadBtn_Next.ThemeName = "Office2007Silver"
        '
        'RadBtn_Cancel
        '
        Me.RadBtn_Cancel.Location = New System.Drawing.Point(25, 13)
        Me.RadBtn_Cancel.Name = "RadBtn_Cancel"
        Me.RadBtn_Cancel.Size = New System.Drawing.Size(75, 23)
        Me.RadBtn_Cancel.TabIndex = 2
        Me.RadBtn_Cancel.Text = "Cancel"
        Me.RadBtn_Cancel.ThemeName = "Office2007Silver"
        '
        'RadBtn_Finish
        '
        Me.RadBtn_Finish.Location = New System.Drawing.Point(791, 13)
        Me.RadBtn_Finish.Name = "RadBtn_Finish"
        Me.RadBtn_Finish.Size = New System.Drawing.Size(75, 23)
        Me.RadBtn_Finish.TabIndex = 3
        Me.RadBtn_Finish.Text = "Finish"
        Me.RadBtn_Finish.ThemeName = "Office2007Silver"
        '
        'RadBtn_Previous
        '
        Me.RadBtn_Previous.Location = New System.Drawing.Point(355, 13)
        Me.RadBtn_Previous.Name = "RadBtn_Previous"
        Me.RadBtn_Previous.Size = New System.Drawing.Size(75, 23)
        Me.RadBtn_Previous.TabIndex = 1
        Me.RadBtn_Previous.Text = "<< Previous"
        Me.RadBtn_Previous.ThemeName = "Office2007Silver"
        '
        'pnlSetupWizardFormButtom
        '
        Me.pnlSetupWizardFormButtom.BackColor = System.Drawing.Color.Transparent
        Me.pnlSetupWizardFormButtom.Controls.Add(Me.RadBtn_Cancel)
        Me.pnlSetupWizardFormButtom.Controls.Add(Me.RadBtn_Next)
        Me.pnlSetupWizardFormButtom.Controls.Add(Me.RadBtn_Previous)
        Me.pnlSetupWizardFormButtom.Controls.Add(Me.RadBtn_Finish)
        Me.pnlSetupWizardFormButtom.Dock = System.Windows.Forms.DockStyle.Bottom
        Me.pnlSetupWizardFormButtom.Location = New System.Drawing.Point(1, 623)
        Me.pnlSetupWizardFormButtom.Name = "pnlSetupWizardFormButtom"
        Me.pnlSetupWizardFormButtom.Size = New System.Drawing.Size(898, 46)
        Me.pnlSetupWizardFormButtom.TabIndex = 1
        Me.pnlSetupWizardFormButtom.TabStop = False
        Me.pnlSetupWizardFormButtom.ThemeName = "ControlDefault"
        '
        'mainPanel
        '
        Me.mainPanel.Dock = System.Windows.Forms.DockStyle.Fill
        Me.mainPanel.Location = New System.Drawing.Point(1, 24)
        Me.mainPanel.Name = "mainPanel"
        Me.mainPanel.Size = New System.Drawing.Size(898, 599)
        Me.mainPanel.TabIndex = 5
        '
        'WizardFormBase
        '
        Me.AutoScaleDimensions = New System.Drawing.SizeF(6.0!, 13.0!)
        Me.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font
        Me.ClientSize = New System.Drawing.Size(900, 670)
        Me.ControlBox = False
        Me.Controls.Add(Me.mainPanel)
        Me.Controls.Add(Me.pnlSetupWizardFormButtom)
        Me.KeyPreview = True
        Me.MaximizeBox = False
        Me.MaximumSize = New System.Drawing.Size(900, 700)
        Me.MinimizeBox = False
        Me.MinimumSize = New System.Drawing.Size(575, 325)
        Me.Name = "WizardFormBase"
        Me.ShowIcon = False
        Me.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen
        Me.Text = "Setup Wizard"
        Me.Controls.SetChildIndex(Me.pnlSetupWizardFormButtom, 0)
        Me.Controls.SetChildIndex(Me.mainPanel, 0)
        CType(Me.RadBtn_Next, System.ComponentModel.ISupportInitialize).EndInit()
        CType(Me.RadBtn_Cancel, System.ComponentModel.ISupportInitialize).EndInit()
        CType(Me.RadBtn_Finish, System.ComponentModel.ISupportInitialize).EndInit()
        CType(Me.RadBtn_Previous, System.ComponentModel.ISupportInitialize).EndInit()
        CType(Me.pnlSetupWizardFormButtom, System.ComponentModel.ISupportInitialize).EndInit()
        Me.pnlSetupWizardFormButtom.ResumeLayout(False)
        Me.pnlSetupWizardFormButtom.PerformLayout()
        Me.ResumeLayout(False)
        Me.PerformLayout()

    End Sub
    Friend WithEvents RadBtn_Next As Telerik.WinControls.UI.RadButton
    Friend WithEvents RadBtn_Cancel As Telerik.WinControls.UI.RadButton
    Friend WithEvents RadBtn_Finish As Telerik.WinControls.UI.RadButton
    Friend WithEvents RadBtn_Previous As Telerik.WinControls.UI.RadButton
    Friend WithEvents pnlSetupWizardFormButtom As Telerik.WinControls.UI.RadPanel
    Friend WithEvents mainPanel As System.Windows.Forms.Panel
End Class

Hope this helps.

Seth



回答4:

I did it the following. Had a flag if the selected tab was being changed from the code. If so I allowed it or else it is disallowed. The following code worked fine for me.

    Private _selectedTabBeingChangedFromCode As Boolean

    Private Function IsDesignMode() As Boolean
        Return Me.Site IsNot Nothing AndAlso Me.Site.DesignMode = True
    End Function

    Public Shadows Property SelectedTab() As System.Windows.Forms.TabPage
        Get
            Return MyBase.SelectedTab
        End Get
        Set(ByVal value As System.Windows.Forms.TabPage)
            _selectedTabBeingChangedFromCode = True
            MyBase.SelectedTab = value
            _selectedTabBeingChangedFromCode = False
        End Set
    End Property

    Private Sub WizardTabControl_Selecting(ByVal sender As Object, ByVal e As System.Windows.Forms.TabControlCancelEventArgs) Handles Me.Selecting
        If IsDesignMode() Then
            Return
        End If

        If _selectedTabBeingChangedFromCode = False Then
            e.Cancel = True
        End If
    End Sub