KnowDotNet NetRefactor

Make A Dockable Toolbar Like the Office Toolbar

by Les Smith and Brian Davis
Print this Article Discuss in Forums

I need a toolbar to act like an Office Toolbar.  How do I make a form resize and snap to the respective screen border as it moves outside of the bounds of the screen.  I also need to relocate the buttons on the toolbar.

You would think this would be a simple task, and althought it is not rocket science, it does require some tricky code.  If you do not get the code just right, you will get more "flash for the audience" than you want.  For this reason, you will see a "Busy" flag being set that keeps the flash from happening.  The Mosue_Move event will be firing so rapidily that you must protect the code that is moving the toolbar while it is in control.  For this reason, if the truth were known, the Busy flag is causing me to ignore most of the Mouse_Move events.  However, that will not be noticeable in the operation of moving the toolbar.  This article will show you how to make a user designed toolbar automatically resize from a vertical to a horizontal position, relocate all of its tool buttons, and snap to the screen border as the toolbar is moved outside the bounds of the screen on any border.

First, you need to build a form like the one shown in Figure 1.  The number of buttons is up to you.  However they should all be the same size, including the lblMove Label, which has the move icon on it.  The user must click on that control to move the form.  Set the FormBorderStyle property to
SizableToolWindow.  If you use any one of the others, either it will not look good or it will not be able to be sized as narrow as I want it to be for a vertical toolbar.

Figure 1 - Docking Toolbar.

Docking Toolbar Design


There are several sections of code that I will reproduce in the article so that you have all of the code, except for the Window Designer Code.

The declarations section of the form must have the variables shown below.  I use the Enums so that I can get Intellisense and make the code more readable.

   Private formLoading As Boolean = True
   Private XPos As Long
   Private YPos As Long
   Private busy As Boolean
   Private vWidth As Single = 32
  
Private vHeight As Integer = 160
  
Private hWidth As Integer = 160
  
Private hHeight As Integer = 32
  
Private btnHeight As Integer = 25
  
Private btnWidth As Integer = 25
  
Private currDockStyle As Short
   Private Enum DockPosition
      Top
      Bottom
      Left
      Right
  
End Enum
   Private Enum DockStyle
      NotSet
      Vertical
      Horizontal
  
End Enum

The event handlers for the lblMove and mouse events are shown below.  The Mouse_Move event determines when the toolbar has been moved outside of the bounds of the screen, on any of the four borders.  When this happens, the DockToolBar method is called to snap the form to the border that the toobar has just violated.

   Private Sub Form1_Resize(ByVal sender As Object, _
      
ByVal e As System.EventArgs) Handles MyBase.Resize
      
' since only a few border styles will support a form as
      ' narrow as this one and sizable tool window is the best
      ' style to use, we must have a resize event in case the user
      ' has nothing else to do but to try to make the form look
      ' cruddy by resizing it, we force the size here...
      If currDockStyle = DockStyle.NotSet Then
         Exit Sub
      ElseIf currDockStyle = DockStyle.Vertical Then
         Me.Height = vHeight
        
Me.Width = vWidth
      
Else
         Me.Height = hHeight
        
Me.Width = hWidth
      
End If
   End Sub

   Private Sub Button4_Click(ByVal sender As System.Object, _
      
ByVal e As System.EventArgs) Handles Button4.Click
      
Me.Dispose()
  
End Sub

   Private Sub lblMove_MouseDown(ByVal sender As Object, _
      
ByVal e As System.Windows.Forms.MouseEventArgs) _
      
Handles lblMove.MouseDown
      XPos = e.X
      YPos = e.Y
  
End Sub


   Private Sub lblMove_MouseMove(ByVal sender As Object, _
      
ByVal e As System.Windows.Forms.MouseEventArgs) _
      
Handles lblMove.MouseMove
      
Dim computedLeft As Single = Me.Left
      
Dim computedTop As Single = Me.Top
      
Dim computedRight As Single = (Me.Left + Me.Width)
      
Dim computedBottom As Single = (Me.Top + Me.Height)

      
If e.Button = MouseButtons.Left Then
         If busy Then Exit Sub

         busy = True

         computedLeft = computedLeft - (XPos - e.X)
         computedTop = computedTop - (YPos - e.Y)
         computedRight = computedRight - (XPos - e.X)
         computedBottom = computedBottom - (YPos - e.Y)

        
If computedLeft >= 1 And _
            computedRight <= _<BR>             Screen.PrimaryScreen.Bounds.Width - 1
Then
            Me.Left = Me.Left - (XPos - e.X)
        
Else
            If Not computedLeft >= 1 Then
               DockToolBar(DockPosition.Left)
            
Else
               DockToolBar(DockPosition.Right)
            
End If
            busy = True
            Exit Sub
         End If
         If computedTop >= 1 And _
            computedBottom <= _<BR>             Screen.PrimaryScreen.Bounds.Height - 1
Then
            Me.Top = Me.Top - (YPos - e.Y)
        
Else
            If Not computedTop >= 1 Then
               DockToolBar(DockPosition.Top)
            
Else
               DockToolBar(DockPosition.Bottom)
            
End If
            busy = True
            Exit Sub
         End If

         busy = False
      End If
   End Sub

   Private Sub Form1_Load(ByVal sender As Object, _
    
ByVal e As System.EventArgs) Handles MyBase.Load
      currDockStyle = DockStyle.Horizontal
  
End Sub

   Private Sub lblMove_MouseUp(ByVal sender As Object, _
      
ByVal e As System.Windows.Forms.MouseEventArgs) _
      
Handles lblMove.MouseUp
      busy =
False
   End Sub

The DockToolBar method does the actual work of redrawing the toolbar and relocating its constituent controls.  The Mouse_Move event passes a parameter that tells the method what type of toolbar is to be drawn, vertical or horizontal, and the position that the redrawn toolbar is to take.  You will notice that the code uses the Screen.PrimaryScreen.GetWorkingArea so that the TaskBar will be honored if AutoHide is not set.

   Private Sub DockToolBar(ByVal DockTo As DockPosition)
      
Dim c As Control
      
Dim s As Single = 0
      
Dim i As Integer = 0
      
With Me
         Dim pt As New System.Drawing.Point(Me.Left, Me.Top)
        
Select Case DockTo
            
Case DockPosition.Top, DockPosition.Bottom
              
Me.currDockStyle = DockStyle.Horizontal
              
' make the toolbar a horizontal bar
               .Width = hWidth
               .Height = hHeight
              
' relocate all controls except the move label
               For Each c In Controls
                  
If Not c Is lblMove Then
                     c.Top = 0
                     c.Left = (i * (btnWidth + 5))
                     i += 1
                     DoEvents()
                  
End If
               Next
               Me.lblMove.Left = Me.Width - (25 + 5)
               .lblMove.Top = 0
               DoEvents()
              
' honor the taskbar if not auto hidden
               If DockTo = DockPosition.Top Then
                  .Top = 1
              
Else
                  .Top = Screen.PrimaryScreen.GetWorkingArea(pt).Height - _
                     (.Height + 1)
              
End If
               .Left = Math.Min(.Left, _
                  Screen.PrimaryScreen.GetWorkingArea(pt).Width - _
                  (.Width + 1))
            
Case DockPosition.Left, DockPosition.Right
               currDockStyle = DockStyle.Vertical
              
' redraw the form as vertical toolbar
               .Width = vWidth
               .Height = vHeight
              
' relocate all of the controls except the move label
               For Each c In Controls
                  
If Not c Is lblMove Then
                     c.Left = 0
                     c.Top = (i * (btnHeight + 5))
                     i += 1
                     DoEvents()
                  
End If
               Next
               ' relocate the move label
               .lblMove.Left = 0
               .lblMove.Top = .Height - (25 + 5)
              
' this code honors any on top task bar if not auto hidden
               If DockTo = DockPosition.Left Then
                  .Left = 1
              
Else
                  .Left = Screen.PrimaryScreen.GetWorkingArea(pt).Width - _
                     (.Width + 1)
              
End If
               .Top = Math.Min(.Top, _
                  Screen.PrimaryScreen.GetWorkingArea(pt).Height - _
                  (.Height + 1))
        
End Select
      End With
   End Sub


As the toolbar is dragged toward a screen border, and crosses the border, it will automatically morph from Vertical to Horizontal as shown in Figure 2 and Figure 3.

Figure 2 - Horizontal Toolbar.

Horizontal Toolbar


Figure 3 - Vertical Toolbar.

Vertical Toolbar


Top Of Form

Writing Add-Ins for Visual Studio .NET
Writing Add-ins for Visual Studio .NET
by Les Smith
Apress Publishing