Verticle Tab Control

JuggaloBrotha

VB.NET Forum Moderator
Staff member
Joined
Jun 3, 2004
Messages
4,530
Location
Lansing, MI; USA
Programming Experience
10+
VB.NET:
Option Explicit On
Option Strict On
Option Infer Off

Imports System.ComponentModel

<System.Diagnostics.DebuggerStepThrough()> _
Public Class VertTabControl
    Inherits System.Windows.Forms.TabControl

#Region " Variables "
    Private _TabSize As Size

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

    Private _HotBrush As SolidBrush
#End Region
#Region " Constructors "
    Public Sub New()
        MyBase.Alignment = TabAlignment.Left
        MyBase.DrawMode = TabDrawMode.OwnerDrawFixed
        MyBase.SizeMode = TabSizeMode.Fixed
        MyBase.ItemSize = New Size(23I, 73I)
        MyBase.HotTrack = True
        MyBase.Multiline = True
        Me._TabSize = New Size(73I, 23I)
        Me._HotBrush = New SolidBrush(_HotColor)
    End Sub
#End Region
#Region " Properties "
    <Browsable(False), EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), _
    DefaultValue(True)> _
    Public Shadows ReadOnly Property Multiline() As Boolean
        Get
            Return MyBase.Multiline
        End Get
    End Property

    <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._HotBrush.Dispose()
                Me._HotColor = value
                Me._HotBrush = New SolidBrush(value)
            End If
        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_Disposed(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Disposed
        Me._HotBrush.Dispose()
    End Sub

    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 Me.HotTrack = True AndAlso e.Index = Me._HotIndex Then
            e.Graphics.DrawString(Me.TabPages(e.Index).Text, Me.Font, Me._HotBrush, 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
        sf.Dispose()
    End Sub

    Private Sub VertTabControl_MouseLeave(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.MouseLeave
        If Me.HotTrack = True AndAlso 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
        If Me.HotTrack = True Then
            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 If
    End Sub

#End Region

End Class
To use it in VS 2005 simply comment the 'Option Infer Off' line out and you're set.
 
Those "New SolidBrush" and "New StringFormat" in DrawItem handler need to be disposed! Instances of Graphics classes that you create almost always needs disposing after use because they implement IDisposable and often use unmanaged resources, which ties not only your own applications resources but also consumes limited OS resources. Painting events are also very vulnerable because they are called so many times during runtime. Not doing clean coding here can lead to random and non-traceable application crashes, usually "out of memory" and similar exceptions. You can also cache such resources and implement IDisposable to clean up when control disposes, this can in some cases be a better option than keep creating these expensive objects in the fast paced Paint events. In this case you only really need a new brush when HotTrackColor property is set to a new value, but it is a balance between keeping a resource and the time it takes to create and dispose it.
 
Those "New SolidBrush" and "New StringFormat" in DrawItem handler need to be disposed! Instances of Graphics classes that you create almost always needs disposing after use because they implement IDisposable and often use unmanaged resources, which ties not only your own applications resources but also consumes limited OS resources. Painting events are also very vulnerable because they are called so many times during runtime. Not doing clean coding here can lead to random and non-traceable application crashes, usually "out of memory" and similar exceptions. You can also cache such resources and implement IDisposable to clean up when control disposes, this can in some cases be a better option than keep creating these expensive objects in the fast paced Paint events. In this case you only really need a new brush when HotTrackColor property is set to a new value, but it is a balance between keeping a resource and the time it takes to create and dispose it.
I totally forgot that I was creating a new brush there so I didn't think to check for a brush cleanup. I've changed the code in the previous post.

Edit:
It's all fixed up now, even the HotTrack property (when set to false it would still hottrack the tabs)

See the original post for the code.
 
Back
Top