Introduction
This article is for those of you who want to use the VS.NET MDI style navigation bar.
Background
This control was made when I was thinking how I could make a good control in VB.NET and not the overrated C#.
Using the code
OK, here's the first part of the control: the VSMDINav
class.
Public Class VSMDINav
Inherits System.Windows.Forms.UserControl
Private Const m_BottomPadd = 5
Private Const m_Height = 25
Private Const m_DefaultWidth = 100
Private Const m_CellPadding = 12
Private m_OffColor As Color = SystemColors.Window
Private m_OnColor As Color = SystemColors.Control
Dim m_Panel As New Panel()
Dim m_Navbuttons As New Panel()
Dim m_NavBackbutton As New Panel()
Dim m_NavNextbutton As New Panel()
Dim m_NavClosebuttons As New Panel()
Dim m_AutoHide As Boolean = True
Dim m_AutoActivate As Boolean = True
Dim m_ShowIcon As Boolean = False
OK, most of those variables are pretty much self explanatory. Now, here are the public properties.
Public Property ShowIcon() As Boolean
Get
Return m_ShowIcon
End Get
Set(ByVal value As Boolean)
m_ShowIcon = value
SetShowIcon()
End Set
End Property
Public Property AutoActivate() As Boolean
Get
Return m_AutoActivate
End Get
Set(ByVal value As Boolean)
m_AutoActivate = value
SetAutoActive()
End Set
End Property
Public Property AutoHide() As Boolean
Get
Return m_AutoHide
End Get
Set(ByVal value As Boolean)
m_AutoHide = value
End Set
End Property
Public Property Tabhost() As Panel
Get
Return m_Panel
End Get
Set(ByVal value As Panel)
m_Panel = value
End Set
End Property
Now begins the main coding. Here are the SetAutoActive()
and the SetShowIcon()
Sub
s.
Private Sub SetAutoActive()
Dim cPage As VSMDINavPage
For Each cPage In m_Panel.Controls
cPage.ShowIcon = m_ShowIcon
Next
End Sub
Private Sub SetShowIcon()
Dim cPage As VSMDINavPage
For Each cPage In m_Panel.Controls
cPage.AutoActivate = m_AutoActivate
Next
End Sub
Then this small piece of code to handle any resizing of the control:
Private Sub VSMDINav_Resize(ByVal sender As Object, _
ByVal e As System.EventArgs) Handles MyBase.Resize
Me.Height = m_Height
End Sub
Next we add the AddPage
Sub
, which basically adds more pages to the nav bar control.
Public Sub AddPage(ByVal sCaption As String, ByVal mForm As Form)
Dim cPage As New VSMDINavPage()
Dim PageRect As Rectangle = getlabelRect(sCaption)
Debug.WriteLine("adding rect: " & PageRect.Left)
cPage.Text = sCaption
cPage.AttachedForm = mForm
cPage.OffColor = m_OffColor
cPage.OnColor = m_OnColor
cPage.Left = PageRect.Left
cPage.Top = PageRect.Top
cPage.Width = PageRect.Width
cPage.Height = PageRect.Height
cPage.Host = Me
cPage.BackColor = m_OffColor
cPage.AutoActivate = m_AutoActivate
cPage.ShowIcon = m_ShowIcon
AddHandler mForm.Activated, AddressOf MDIActive
AddHandler mForm.Closing, AddressOf MDIClose
m_Panel.Controls.Add(cPage)
cPage.SendToBack()
m_Panel.Width = cPage.Left + cPage.Width
checkAutohide()
End Sub
Now for the reverse, removing pages:
Public Sub RemovePage(ByVal mForm As Form)
Dim cPage As VSMDINavPage
Dim cOldPage As VSMDINavPage
Dim I As Integer
For I = m_Panel.Controls.Count - 1 To 0 Step -1
If TypeOf m_Panel.Controls(I) Is VSMDINavPage Then
cPage = m_Panel.Controls(I)
If cPage.AttachedForm.Handle.ToString = mForm.Handle.ToString Then
m_Panel.Controls.Remove(cPage)
End If
End If
Next
If m_Panel.Controls.Count > 0 Then
For Each cPage In m_Panel.Controls
If cOldPage Is Nothing Then
cPage.Left = 0
Else
If cPage.Left > 0 Then
cPage.Left = cOldPage.Left + cOldPage.Width
End If
End If
cOldPage = cPage
Next
End If
checkAutohide()
End Sub
And finally, what to do if a page is selected:
Public Sub SelectPage(ByVal mForm As Form)
Dim cPage As VSMDINavPage
Dim cOldPage As VSMDINavPage
Dim I As Integer
If m_Panel.Controls.Count > 0 Then
For Each cPage In m_Panel.Controls
cPage.Selected = False
Next
For Each cPage In m_Panel.Controls
If cPage.AttachedForm.Handle.ToString = mForm.Handle.ToString Then
cPage.Selected = True
ForceRepaint(cPage)
Exit For
End If
Next
End If
End Sub
Now, we must add the MDIActivate
and MDIClose
, and 'check for updates' Sub
s:
Private Sub MDIActive(ByVal sender As Object, ByVal e As System.EventArgs)
SelectPage(sender)
End Sub
Private Sub MDIClose(ByVal sender As Object, _
ByVal e As System.ComponentModel.CancelEventArgs)
RemovePage(sender)
End Sub
Private Sub checkAutohide()
If m_AutoHide = False Then Exit Sub
If m_Panel.Controls.Count = 0 Then
Me.Visible = False
Else
Me.Visible = True
End If
End Sub
Finally, the finishing touches, repainting and getting the label titles.
Private Function getlabelRect(ByVal stext As String) As Rectangle
Dim cPage As VSMDINavPage
Dim Rect As Rectangle
If m_Panel.Controls.Count = 0 Then
Rect = New Rectangle(0, 0, GetPageWidth(stext), m_Panel.Height)
Else
For Each cPage In m_Panel.Controls
Rect = New Rectangle(cPage.Left + cPage.Width, _
cPage.Top, GetPageWidth(stext), m_Panel.Height)
Next
End If
getlabelRect = Rect
End Function
Private Function GetPageWidth(ByVal stext As String) As Integer
Dim G As Graphics
Dim sSizeF As SizeF
If stext.Length = 0 Then
GetPageWidth = m_DefaultWidth
Exit Function
End If
G = Me.CreateGraphics
sSizeF = G.MeasureString(stext, Me.Font)
GetPageWidth = sSizeF.Width + m_CellPadding
G.Dispose()
End Function
Public Function ForceRepaint(ByVal selPage As VSMDINavPage)
Dim cPage As VSMDINavPage
Me.Invalidate()
For Each cPage In m_Panel.Controls
cPage.Selected = False
Next
For Each cPage In m_Panel.Controls
cPage.Invalidate()
Next
selPage.Selected = True
End Function
Here, we will add all the important paint stuff:
Private Sub Panel1_Paint(ByVal sender As System.Object, _
ByVal e As System.Windows.Forms.PaintEventArgs)
Dim BottomRect As New Rectangle(0, Height - m_BottomPadd, Width, Height)
e.Graphics.FillRectangle(New SolidBrush(m_OffColor), e.ClipRectangle)
e.Graphics.FillRectangle(New SolidBrush(m_OnColor), BottomRect)
End Sub
Private Sub VSMDINav_Paint(ByVal sender As Object, _
ByVal e As System.Windows.Forms.PaintEventArgs) _
Handles MyBase.Paint
Dim BottomRect As New Rectangle(0, Height - m_BottomPadd, Width, Height)
e.Graphics.FillRectangle(New SolidBrush(m_OffColor), e.ClipRectangle)
e.Graphics.FillRectangle(New SolidBrush(m_OnColor), BottomRect)
End Sub
Now, to close it up:
Private Sub VSMDINav_Load(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles MyBase.Load
End Sub
End Class
Now that you know all that, just bundle this all in one last class... da da da daaaa... the VSMDINavPage
class.
Public Class VSMDINavPage
Inherits UserControl
#Region " Local Storage and Public Declares "
Private m_OffColor As Color = SystemColors.Window
Private m_OnColor As Color = SystemColors.Control
Private m_AttachedForm As Form = Nothing
Private m_Selected As Boolean = False
Private m_Host As VSMDINav
Dim m_ShowIcon As Boolean = False
Dim m_AutoActivate As Boolean = False
#End Region
#Region " Property Declares "
Public Property AutoActivate() As Boolean
Get
Return m_AutoActivate
End Get
Set(ByVal value As Boolean)
m_AutoActivate = value
End Set
End Property
Public Property ShowIcon() As Boolean
Get
Return m_ShowIcon
End Get
Set(ByVal value As Boolean)
m_ShowIcon = value
End Set
End Property
Public Property Host() As VSMDINav
Get
Return m_Host
End Get
Set(ByVal value As VSMDINav)
m_Host = value
End Set
End Property
Public Property Selected() As Boolean
Get
Return m_Selected
End Get
Set(ByVal value As Boolean)
m_Selected = value
End Set
End Property
Public Property OffColor() As Color
Get
Return m_OffColor
End Get
Set(ByVal value As Color)
m_OffColor = value
End Set
End Property
Public Property OnColor() As Color
Get
Return m_OnColor
End Get
Set(ByVal value As Color)
m_OnColor = value
End Set
End Property
Public Property AttachedForm() As Form
Get
Return m_AttachedForm
End Get
Set(ByVal value As Form)
m_AttachedForm = value
End Set
End Property
#End Region
Private Sub DoAutoActivate()
If m_AutoActivate = False Then Exit Sub
If m_AttachedForm Is Nothing Then Exit Sub
m_AttachedForm.BringToFront()
End Sub
Private Sub VSMDINavPage_Paint(ByVal sender As Object, _
ByVal e As System.Windows.Forms.PaintEventArgs) _
Handles MyBase.Paint
Dim TabRect As New Rectangle(0, 3, Me.Width, Me.Height)
Dim cColor As Color
Dim ctextFont As Font
Dim ctextColor As Color = SystemColors.ControlDark
Dim Ioffset As Integer = 2
Dim cPage As VSMDINavPage
Dim cOldPage As VSMDINavPage
Dim X As Integer
Dim Y As Integer
Dim sSizeF As SizeF
e.Graphics.FillRectangle(New SolidBrush(m_OffColor), e.ClipRectangle)
If m_Selected = True Then
cColor = m_OnColor
Else
cColor = m_OffColor
End If
e.Graphics.FillRectangle(New SolidBrush(cColor), TabRect)
If m_Selected = True Then
cColor = SystemColors.ControlText
ctextFont = New Font(Host.Font, FontStyle.Bold)
ctextColor = SystemColors.ControlText
Ioffset = 0
Else
cColor = SystemColors.ControlDark
ctextFont = New Font(Host.Font, FontStyle.Regular)
ctextColor = SystemColors.ControlDark
Ioffset = 6
End If
e.Graphics.DrawLine(New Pen(cColor, 3), Width, _
TabRect.Top, Width, TabRect.Height - 1)
If Me.Text.Length > 0 Then
sSizeF = e.Graphics.MeasureString(Me.Text, Host.Font)
X = Me.Width / 2
X = X - sSizeF.Width / 2
Y = Me.Height / 2
Y = Y - sSizeF.Height / 2
If m_Selected = True Then
e.Graphics.DrawString(Me.Text, ctextFont, _
New SolidBrush(ctextColor), X - 3, Y)
Else
e.Graphics.DrawString(Me.Text, ctextFont, _
New SolidBrush(ctextColor), X, Y)
End If
End If
ctextFont.Dispose()
If Host.Tabhost.Controls.Count < 2 Then Exit Sub
For Each cPage In Host.Tabhost.Controls
If cPage.Selected = True Then Exit For
cOldPage = cPage
Next
If cOldPage Is Nothing Then
Else
RemoveOldLine(cOldPage)
End If
End Sub
Private Sub RemoveOldLine(ByVal cOldPage As VSMDINavPage)
Dim G As Graphics = cOldPage.CreateGraphics
Dim TabRect As New Rectangle(0, 3, cOldPage.Width, cOldPage.Height)
G.DrawLine(New Pen(m_OffColor, 3), Width, _
TabRect.Top, Width, TabRect.Height - 1)
End Sub
Private Sub VSMDINavPage_Click(ByVal sender As Object, _
ByVal e As System.EventArgs) Handles MyBase.Click
m_Selected = True
Me.Invalidate()
m_Host.ForceRepaint(Me)
DoAutoActivate()
End Sub
End Class
Well, that's about it. Sorry if you don't understand, but if you can't then don't read an advanced article.
Points of Interest
In spite of what many people told me, this control was made in VB.NET, not the overrated C#. They told me it would be near impossible to make it in VB.NET, but it's been done and quite easily. In short, C# is highly overrated.
History
This was made for the express purpose of proving you don't need C# for advanced control creation.