Click here to Skip to main content
15,896,557 members
Articles / Programming Languages / Visual Basic
Article

VS.NET MDI Navigation

Rate me:
Please Sign up or sign in to vote.
1.06/5 (18 votes)
12 Sep 20041 min read 91.8K   468   23   17
Visual Studio .NET MDI navigation bar.

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.

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

VB
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() Subs.

VB
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:

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

VB
Public Sub AddPage(ByVal sCaption As String, ByVal mForm As Form)
        'add a page
        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:

VB
Public Sub RemovePage(ByVal mForm As Form)
       'remove all pages that have mForm as an AttachedForm

       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

       'move the pages into place
       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:

VB
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' Subs:

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

VB
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:

VB
Private Sub Panel1_Paint(ByVal sender As System.Object, _
              ByVal e As System.Windows.Forms.PaintEventArgs)
       'paint the nav buttons
       Dim BottomRect As New Rectangle(0, Height - m_BottomPadd, Width, Height)
       'Paint the backing
       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:

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

VB
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

        'paint the background
        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
            'draw the 3d edge
            cColor = SystemColors.ControlText
            ctextFont = New Font(Host.Font, FontStyle.Bold)
            ctextColor = SystemColors.ControlText
            Ioffset = 0
        Else
            'draw a flat line at the right edge
            cColor = SystemColors.ControlDark

            ctextFont = New Font(Host.Font, FontStyle.Regular)
            ctextColor = SystemColors.ControlDark
            Ioffset = 6
        End If

        'draw spep line
        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()

        'remove the line on the previous page
        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.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
United States United States
I Think im little
crazy.

Comments and Discussions

 
Generalneed solution Pin
bhavini_20077-Aug-08 23:56
bhavini_20077-Aug-08 23:56 
GeneralHere's how to use it... Pin
mpemberton31-Jan-06 9:16
mpemberton31-Jan-06 9:16 
GeneralRe: Here's how to use it... Pin
mpemberton15-Feb-06 15:56
mpemberton15-Feb-06 15:56 
GeneralAnyone figured out to use it Pin
matt991914-Jul-05 3:05
matt991914-Jul-05 3:05 
GeneralNice control - good that you used VB.NET Pin
neuralSea87-Jul-05 17:37
neuralSea87-Jul-05 17:37 
GeneralYou are so arrogant Pin
Trillo15-Jun-05 7:33
Trillo15-Jun-05 7:33 
GeneralRe: You are so arrogant Pin
Brett Clark21-Jun-07 16:59
Brett Clark21-Jun-07 16:59 
QuestionHow to use it? Pin
is_vlb5023-Mar-05 23:59
is_vlb5023-Mar-05 23:59 
GeneralNo screenshot or Demo Project Pin
claytonread2-Jan-05 9:21
claytonread2-Jan-05 9:21 
QuestionHow To Use. Pin
Mr Panic27-Nov-04 18:15
Mr Panic27-Nov-04 18:15 
AnswerRe: How To Use. Pin
Amsterdmr13-Jan-06 17:31
Amsterdmr13-Jan-06 17:31 
Question? Pin
attackweasel13-Sep-04 5:48
attackweasel13-Sep-04 5:48 
AnswerRe: ? Pin
Mr Panic9-Oct-04 14:23
Mr Panic9-Oct-04 14:23 
GeneralDemo Project (must have) Pin
Ashaman13-Sep-04 3:39
Ashaman13-Sep-04 3:39 
GeneralRe: Demo Project (must have) Pin
Alon Ronen27-Oct-04 23:12
Alon Ronen27-Oct-04 23:12 
hear hear.

Must have a demo project and screen shots....

Alon
GeneralRe: Demo project.. Pin
Jabes13-Sep-04 0:31
Jabes13-Sep-04 0:31 
GeneralRe: Demo project.. Pin
vbinfo13-Sep-04 6:34
vbinfo13-Sep-04 6:34 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.