Question Verticle Tab control

JuggaloBrotha

VB.NET Forum Moderator
Staff member
Joined
Jun 3, 2004
Messages
4,530
Location
Lansing, MI; USA
Programming Experience
10+
Has anyone seen a control or know how to make a control/modify the existing tab control to display tabs vertically on the left edge of the control? By vertically I mean the tab text still reads horizontally but are vertically stacked.

Something similar to this (you all should recognize it):
VertTabs.jpg
 
You can do this with the default TabControl, setting Alignment to Left, configure the tab sizes and owner draw the text horizontally. Here's from a post explaining:
Set the Tabcontrols SizeMode to Fixed.
Change the TabControls ItemSize property so that it gets serialized.
Change the TabControls DrawMode to OwnerDrawFixed
Then add the following code (assuming your tabcontrol is called TabControl1)
\\\
VB.NET:
Private Sub TabControl1_DrawItem(ByVal sender As System.Object, _
            ByVal e As DrawItemEventArgs) Handles TabControl1.DrawItem

    e.Graphics.FillRectangle(SystemBrushes.Control, e.Bounds)
    Dim sf As New StringFormat
    sf.Alignment = StringAlignment.Center
    sf.LineAlignment = StringAlignment.Center
    e.Graphics.DrawString(TabControl1.TabPages(e.Index).Text, _
            TabControl1.Font, SystemBrushes.ControlText, _
            RectangleF.op_Implicit(e.Bounds), sf)
End Sub
Note that you swap width/height in ItemSize, for example set width 20 and height 70, and the tabs will stack on top of each others.
 
Next up: HotTracking

When doing the DrawItem event it doesn't matter if HotTracking is set to True or False, it doesn't change the text forecolor when the mouse moves over the text, which is expected because there's no code to handle that, but I'm at a loss for determining which tab has the mouse over it to re-draw it's text in a different color. Any ideas?
 
In MouseMove you can check if any of the tab rectangles contains the mouse location, if so you have the hotindex and Invalidate, example:
VB.NET:
Private hotindex As Integer

Private Sub TabControl1_MouseMove(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles TabControl1.MouseMove
    For i As Integer = 0 To Me.TabControl1.TabCount - 1
        If i <> Me.hotindex Then
            Dim r As Rectangle = Me.TabControl1.GetTabRect(i)
            If r.Contains(e.Location) Then
                Me.TabControl1.Invalidate(Me.TabControl1.GetTabRect(Me.hotindex))
                Me.hotindex = i
                Me.TabControl1.Invalidate(Me.TabControl1.GetTabRect(Me.hotindex))
                Exit For
            End If
        End If
    Next
End Sub
Then in Paint you draw one back color for regular tabs and another one for the hottracked tab.
VB.NET:
If e.Index = hotindex Then
Perhaps you can arrange something in case mouse leaves, too? :)
 
Well I've reached a point where I know that I don't know what I'm doing, here's the control so far:
VB.NET:
Option Explicit On
Option Strict On
Option Infer Off

Imports System.ComponentModel

Public Class VertTabControl
    Inherits System.Windows.Forms.TabControl

    Private _HotIndex As Integer = 0I
    Private _HotColor As Color = Color.Blue

    Public Sub New()
        MyBase.Alignment = TabAlignment.Left
        MyBase.DrawMode = TabDrawMode.OwnerDrawFixed
        MyBase.SizeMode = TabSizeMode.Fixed
        MyBase.ItemSize = New Size(23I, 73I)
    End Sub

#Region " Properties "
    <Browsable(False), EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), _
    DefaultValue(False)> _
    Public Shadows ReadOnly Property DrawMode() As TabDrawMode
        Get
            Return TabDrawMode.OwnerDrawFixed
        End Get
    End Property

    <Browsable(False), EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), _
    DefaultValue(False)> _
    Public Shadows ReadOnly Property Alignment() As TabAlignment
        Get
            Return TabAlignment.Left
        End Get
    End Property

    <Browsable(False), EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), _
    DefaultValue(False)> _
    Public Shadows ReadOnly Property SizeMode() As TabSizeMode
        Get
            Return TabSizeMode.Fixed
        End Get
    End Property

    <Description("Color of the tab text when mouse hovers over it"), DefaultValue("Blue"), Category("Appearance")> _
    Public Property HotTrackColor() As Color
        Get
            Return Me._HotColor
        End Get
        Set(ByVal value As Color)
            If Me._HotColor.Equals(value) = False Then Me._HotColor = value
        End Set
    End Property
#End Region

    Private Sub VertTabControl_DrawItem(ByVal sender As Object, ByVal e As System.Windows.Forms.DrawItemEventArgs) Handles Me.DrawItem
        e.Graphics.FillRectangle(SystemBrushes.Control, e.Bounds)
        Dim sf As New StringFormat
        sf.Alignment = StringAlignment.Center
        sf.LineAlignment = StringAlignment.Center
        If e.Index = Me._HotIndex Then
            e.Graphics.DrawString(Me.TabPages(e.Index).Text, Me.Font, New SolidBrush(_HotColor), RectangleF.op_Implicit(e.Bounds), sf)
        Else
            e.Graphics.DrawString(Me.TabPages(e.Index).Text, Me.Font, SystemBrushes.ControlText, RectangleF.op_Implicit(e.Bounds), sf)
        End If
    End Sub

    Private Sub VertTabControl_MouseLeave(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.MouseLeave
        If Me._HotIndex <> -1I Then
            Me.Invalidate(Me.GetTabRect(Me._HotIndex))
            Me._HotIndex = -1I
        End If
    End Sub

    Private Sub VertTabControl_MouseMove(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles Me.MouseMove
        For i As Integer = 0I To Me.TabCount - 1I
            If i <> Me._HotIndex AndAlso Me._HotIndex <> -1I Then
                If Me.GetTabRect(i).Contains(e.Location) Then
                    Me.Invalidate(Me.GetTabRect(Me._HotIndex))
                    Me._HotIndex = i
                    Me.Invalidate(Me.GetTabRect(Me._HotIndex))
                    Exit For
                End If
            End If
        Next i
    End Sub

    Private Sub VertTabControl_Paint(ByVal sender As Object, ByVal e As System.Windows.Forms.PaintEventArgs) Handles Me.Paint
        'If e.Index = Me._HotIndex Then

        'End If
    End Sub
End Class
The paint event doesn't work for this because Index isn't a member of PaintEventArgs however the DrawItem event does have it and it works to some degree already but if you move the mouse down past the tabs and back up the HotTracking stops working altogether. Any advice?
 
I said "Paint", but I meant DrawItem. Mouse move where there is no tab means "hotindex" is none of the available indexes, can't you use "-1" to tell if there is no current hot tab?
 
I said "Paint", but I meant DrawItem. Mouse move where there is no tab means "hotindex" is none of the available indexes, can't you use "-1" to tell if there is no current hot tab?
That's what I'm doing, turns out I had the <> -1 check in the wrong spot.

This appears to work, if anyone sees something that doesn't work it'd be nice to know about it:
VB.NET:
Option Explicit On
Option Strict On
Option Infer Off

Imports System.ComponentModel

Public Class VertTabControl
    Inherits System.Windows.Forms.TabControl

    Private _TabSize As Size

    Private _HotIndex As Integer = -1I
    Private _HotColor As Color = Color.Blue

    Public Sub New()
        MyBase.Alignment = TabAlignment.Left
        MyBase.DrawMode = TabDrawMode.OwnerDrawFixed
        MyBase.SizeMode = TabSizeMode.Fixed
        MyBase.ItemSize = New Size(23I, 73I)
        Me._TabSize = New Size(73I, 23I)
    End Sub

#Region " Properties "

    <Browsable(False), EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), _
        DefaultValue(False)> _
        Public Shadows ReadOnly Property ItemSize() As Size
        Get
            Return MyBase.ItemSize
        End Get
    End Property

    <Browsable(False), EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), _
    DefaultValue(False)> _
    Public Shadows ReadOnly Property DrawMode() As TabDrawMode
        Get
            Return TabDrawMode.OwnerDrawFixed
        End Get
    End Property

    <Browsable(False), EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), _
    DefaultValue(False)> _
    Public Shadows ReadOnly Property Alignment() As TabAlignment
        Get
            Return TabAlignment.Left
        End Get
    End Property

    <Browsable(False), EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), _
    DefaultValue(False)> _
    Public Shadows ReadOnly Property SizeMode() As TabSizeMode
        Get
            Return TabSizeMode.Fixed
        End Get
    End Property

    <Description("Color of the tab text when mouse hovers over it"), DefaultValue("Blue"), Category("Appearance")> _
    Public Property HotTrackColor() As Color
        Get
            Return Me._HotColor
        End Get
        Set(ByVal value As Color)
            If Me._HotColor.Equals(value) = False Then Me._HotColor = value
        End Set
    End Property

    <Description("The size of the tabs"), DefaultValue("73, 23"), Category("Appearance")> _
        Public Property TabSize() As Size
        Get
            Return Me._TabSize
        End Get
        Set(ByVal value As Size)
            If Me._TabSize <> value Then
                Me._TabSize = value
                MyBase.ItemSize = New Size(Me._TabSize.Height, Me._TabSize.Width)
            End If
        End Set
    End Property

#End Region
#Region " Handled Items "

    Private Sub VertTabControl_DrawItem(ByVal sender As Object, ByVal e As System.Windows.Forms.DrawItemEventArgs) Handles Me.DrawItem
        e.Graphics.FillRectangle(SystemBrushes.Control, e.Bounds)
        Dim sf As New StringFormat
        sf.Alignment = StringAlignment.Center
        sf.LineAlignment = StringAlignment.Center
        If e.Index = Me._HotIndex Then
            e.Graphics.DrawString(Me.TabPages(e.Index).Text, Me.Font, New SolidBrush(_HotColor), RectangleF.op_Implicit(e.Bounds), sf)
        Else
            e.Graphics.DrawString(Me.TabPages(e.Index).Text, Me.Font, SystemBrushes.ControlText, RectangleF.op_Implicit(e.Bounds), sf)
        End If
    End Sub

    Private Sub VertTabControl_MouseLeave(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.MouseLeave
        If Me._HotIndex <> -1I Then
            Me.Invalidate(Me.GetTabRect(Me._HotIndex))
            Me._HotIndex = -1I
        End If
    End Sub

    Private Sub VertTabControl_MouseMove(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles Me.MouseMove
        For i As Integer = 0I To Me.TabCount - 1I
            If i <> Me._HotIndex Then
                If Me.GetTabRect(i).Contains(e.Location) Then
                    If Me._HotIndex <> -1I Then Me.Invalidate(Me.GetTabRect(Me._HotIndex))
                    Me._HotIndex = i
                    Me.Invalidate(Me.GetTabRect(Me._HotIndex))
                    Exit For
                End If
            End If
        Next i
    End Sub

#End Region

End Class
I've also hidden the ItemSize property because it needs to work backwards (IE the width property controls the height of the tab and the height property controls the width of the tab) and I've provided a TabSize property to use instead.
 
Back
Top