Option Strict On
Imports System.ComponentModel
<ToolboxBitmap(GetType(System.Windows.Forms.TabControl))> _
Public Class TabControlEx
Inherits TabControl
Private Declare Auto Function SetParent Lib "user32" (ByVal hWndChild As IntPtr, ByVal hWndNewParent As IntPtr) As IntPtr
Protected CloseButtonCollection As New Dictionary(Of Button, TabPage)
Private _ShowCloseButtonOnTabs As Boolean = True
Protected LabelCollection As New Dictionary(Of Label, TabPage)
<Browsable(True), DefaultValue(True), Category("Behavior"), Description("Indicates whether a close button should be shown on each TabPage")> _
Public Property ShowCloseButtonOnTabs() As Boolean
Get
Return _ShowCloseButtonOnTabs
End Get
Set(ByVal value As Boolean)
_ShowCloseButtonOnTabs = value
For Each btn In CloseButtonCollection.Keys
btn.Visible = _ShowCloseButtonOnTabs
Next
RePositionCloseButtons()
End Set
End Property
Protected Overrides Sub OnCreateControl()
MyBase.OnCreateControl()
RePositionCloseButtons()
End Sub
Protected Overrides Sub OnControlAdded(ByVal e As System.Windows.Forms.ControlEventArgs)
MyBase.OnControlAdded(e)
Dim tp As TabPage = DirectCast(e.Control, TabPage)
Dim rect As Rectangle = Me.GetTabRect(Me.TabPages.IndexOf(tp))
Dim btn As Button = AddCloseButton(tp)
AddHandler btn.MouseEnter, AddressOf OnBtnMouseEnter
AddHandler btn.MouseLeave, AddressOf OnBtnMouseLeave
AddHandler btn.MouseDown, AddressOf onBtnMouseDown
btn.Size = New Size(rect.Height - 1, rect.Height - 1)
btn.Location = New Point(rect.X + rect.Width - rect.Height - 1, rect.Y + 1)
SetParent(btn.Handle, Me.Handle)
AddHandler btn.Click, AddressOf OnCloseButtonClick
CloseButtonCollection.Add(btn, tp)
Dim TabName As Label = AddLabel(tp)
TabName.Size = New Size(rect.Width - btn.Width - 10, rect.Height)
TabName.Location = New Point(rect.X + 10, rect.Y + 1)
SetParent(TabName.Handle, Me.Handle)
AddHandler TabName.MouseDown, AddressOf onLabelMouseDown
LabelCollection.Add(TabName, tp)
End Sub
Protected Overrides Sub OnControlRemoved(ByVal e As System.Windows.Forms.ControlEventArgs)
Dim btn As Button = CloseButtonOfTabPage(DirectCast(e.Control, TabPage))
RemoveHandler btn.Click, AddressOf OnCloseButtonClick
CloseButtonCollection.Remove(btn)
SetParent(btn.Handle, Nothing)
btn.Dispose()
MyBase.OnControlRemoved(e)
Dim lbl As Label = LabelOfTabPage(DirectCast(e.Control, TabPage))
SetParent(lbl.handle, Nothing)
lbl.Dispose()
MyBase.OnControlRemoved(e)
End Sub
Protected Overrides Sub OnLayout(ByVal levent As System.Windows.Forms.LayoutEventArgs)
MyBase.OnLayout(levent)
RePositionCloseButtons()
End Sub
Protected Overridable Sub onLabelMouseDown(ByVal sender As Object, ByVal e As EventArgs)
If Me.TabCount > 1 Then
Dim TabName As Label = DirectCast(sender, Label)
Dim x As Integer = TabName.Location.X
'you can also enter the tab width in manually, mine was 115
x = x \ Me.ItemSize.Width
Me.SelectedTab = Me.TabPages(x)
End If
End Sub
Public Event CloseButtonClick As CancelEventHandler
Protected Overridable Sub OnCloseButtonClick(ByVal sender As Object, ByVal e As EventArgs)
If Me.TabCount > 1 Then
If Not DesignMode Then
Dim btn As Button = DirectCast(sender, Button)
Dim tp As TabPage = CloseButtonCollection(btn)
Dim lastTab As Integer
Dim closeIndex As Integer = btn.Location.X \ Me.ItemSize.Width
If closeIndex >= Me.SelectedIndex And Me.SelectedIndex <> Me.TabCount - 1 Then
lastTab = Me.SelectedIndex
ElseIf closeIndex < Me.SelectedIndex Then
lastTab = Me.SelectedIndex - 1
ElseIf closeIndex = Me.SelectedIndex And Me.SelectedIndex <> 0 And Me.SelectedIndex <> Me.TabCount - 1 Then
lastTab = Me.SelectedIndex + 1
ElseIf closeIndex = Me.SelectedIndex AndAlso Me.SelectedIndex = Me.TabCount - 1 Then
lastTab = Me.SelectedIndex - 1
End If
Dim ee As New CancelEventArgs
RaiseEvent CloseButtonClick(sender, ee)
If Not ee.Cancel Then
Me.TabPages.Remove(tp)
RePositionCloseButtons()
End If
Me.SelectedIndex = lastTab
End If
End If
End Sub
Protected Overridable Function AddLabel(ByVal tp As TabPage) As Label
Dim tablabel As New Label
With tablabel
.BackColor = Color.Transparent
.Font = Me.Font
.Text = tp.Text
End With
Return tablabel
End Function
Protected Overridable Function AddCloseButton(ByVal tp As TabPage) As Button
Dim closeButton As New Button
With closeButton
.FlatStyle = FlatStyle.Flat
.Text = "X"
.BackColor = Color.Transparent
.FlatAppearance.BorderSize = 0
.ForeColor = Color.Black
.Font = New Font("Microsoft Sans Serif", 6, FontStyle.Bold)
End With
Return closeButton
End Function
Public Sub RePositionCloseButtons()
For Each item In CloseButtonCollection
RePositionCloseButtons(item.Value)
Next
For Each item In LabelCollection
RePositionCloseButtons(item.Value)
Next
End Sub
Public Sub RePositionCloseButtons(ByVal tp As TabPage)
Dim btn As Button = CloseButtonOfTabPage(tp)
If btn IsNot Nothing Then
Dim tpIndex As Integer = Me.TabPages.IndexOf(tp)
If tpIndex >= 0 Then
Dim rect As Rectangle = Me.GetTabRect(tpIndex)
If Me.SelectedTab Is tp Then
btn.Size = New Size(rect.Height - 1, rect.Height - 1)
btn.Location = New Point(rect.X + rect.Width - rect.Height, rect.Y + 1)
Else
btn.BackgroundImage = My.Resources.btnBack
btn.BackgroundImageLayout = ImageLayout.Stretch
btn.Size = New Size(rect.Height - 2, rect.Height - 2)
btn.Location = New Point(rect.X + rect.Width - rect.Height - 1, rect.Y + 1)
End If
Dim TabName As Label = LabelOfTabPage(tp)
If TabName IsNot Nothing Then
If tpIndex >= 0 Then
If Me.SelectedTab Is tp Then
TabName.Size = New Size(rect.Width - btn.Width - 10, rect.Height)
TabName.Location = New Point(rect.X + 10, rect.Y + 1)
Else
TabName.BackgroundImage = My.Resources.btnBack
TabName.BackgroundImageLayout = ImageLayout.Stretch
TabName.Size = New Size(rect.Width - btn.Width - 13, rect.Height - 1)
TabName.Location = New Point(rect.X + 10, rect.Y + 1)
End If
End If
TabName.Text = tp.Text
TabName.BringToFront()
End If
If Me.TabCount > 1 Then
btn.Visible = ShowCloseButtonOnTabs
Else
btn.Visible = False
TabName.Size = New Size(rect.Width - 10, rect.Height)
End If
btn.BringToFront()
End If
End If
End Sub
Protected Function CloseButtonOfTabPage(ByVal tp As TabPage) As Button
Return (From item In CloseButtonCollection Where item.Value Is tp Select item.Key).FirstOrDefault
End Function