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?
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.
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.
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
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