VS.NET like toolbox control






4.73/5 (11 votes)
Feb 13, 2004
4 min read

126095

868
Collapsing and expanding panel toolbar
Simple ToolBar control
As I was working on my editor I decided I needed a toolbox control like the one in VS.NET. Instead of going out and trying to find a control on the net that wouldn't do what I wanted anyway, I thought it would be fun just to roll my own.
Overview
This control inherits from the panel object. Basically what it does is draw, position, collapse and expand the panels on screen to get the effect you want out of the control. You can also add any other control (textbox in the case of the demo) you want to the panel object.
Each ToolBar contains multiple ToolPanel's.
Methods and Properties
ToolBar.new([dockStyle], [width]) |
Overloaded Constructor | Creates the ToolBar object |
ToolBar.count[ReadOnly] |
Property | Returns the number of panels |
ToolBar.item(integer) |
Property | Returns a panel object |
ToolBar.Remove(string) |
Method | Removes a panel |
ToolBar.View(Object) |
Method | Brings a panel into view |
ToolBar.Add(String) |
Method | Adds a panel |
ToolPanel.new(String) |
Constructor | Creates a ToolPanel object |
ToolPanel.Title[ReadOnly] |
Property | Returns the Title |
ToolPanel.State[ReadOnly] |
Property/Enum | Returns the object state |
ToolPanel.captionArea[ReadOnly] |
Property | Returns a rectangle of the caption dimensions |
ToolPanel.clientArea[ReadOnly] |
Property | Returns a rectangle of the client dimensions |
ToolPanel.captionHeight(integer) |
Property | Returns/Sets the size of the caption |
ToolPanel.Maximize |
Method | Although public do not call this, resets the docks |
ToolPanel.Minimize(Object) |
Method | Although public do not call this, resets the docks |
Simple Example
The first thing you need to do is import the namespace. This can be done at the top of your code by typing Imports Toolbar. The next thing you will need to do is declare an instance of the object and add it to your form. There are a bunch of ways to do this, you can declare it as a Friend and add it as a control in initialize components or you can simply declare it as public in the main body of your code. What solution is right for you depends on if it needs to dock in a certain order on your form. For instance if you have a statusbar on your form you will want to declare it as a friend and add it to the initialization. Here are both examples:
Initialization method:
Private components As System.ComponentModel.IContainer
...
...
...
Friend Toolbar As New Toolbar.Initalize(DockStyle.Left, 175)
Friend WithEvents Textbox2 As System.Windows.Forms.TextBox
Friend WithEvents Button2 As System.Windows.Forms.Button
Friend WithEvents Label2 As System.Windows.Forms.Label
<System.Diagnostics.DebuggerStepThrough()>
Private Sub InitializeComponent()
...
...
...
Me.Controls.Add(Me.Toolbar)
Me.Controls.Add(Me.Textbox2)
Me.Controls.Add(Me.Button2)
...
...
...
Public Method:
Public Toolbar as new toolbar()
Now that you have created the object you can use it by calling toolbar.add("My Tab")
like this..
Private Sub Button1_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles Button1.Click
Toolbar.add(TextBox.Text)
End Sub
While this is functional its not very interesting, to spice things up a bit we need to add another control to the panel (no since having an empty panel). For this I have created another class called panelContents
. You do not need to do it this way, you could just use Controls.Add(new textbox)
. Lets take a look at the code:
' This class just creates a textbox object already
' loaded with a few options
'
Public Class panelContents : Inherits TextBox
Sub New(ByVal txt As String)
Me.Multiline = True
Me.Dock = DockStyle.Fill
Me.BorderStyle = BorderStyle.None
Me.ScrollBars = ScrollBars.Vertical
Me.Text = txt
End Sub
End Class
Private Sub Button1_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles Button1.Click
' Create a new Panel
Dim thisPanel As ToolPanel = Toolbar.add(TextBox.Text)
' Put a textbox in the panel
thisPanel.Controls.Add(New panelContents("This is " & TextBox.Text))
End Sub
One more thing of noted importance is the Remove method. You can call this in you program like so
Private Sub Button2_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles Button2.Click
Toolbar.Remove(Textbox2.Text)
End Sub
ToolBar Class
I am going to skip the overloaded new constructors and the properties for the class and focus on the important things, namely add, remove and view. There is one important thing you need to know before we gets started, all the ToolBerPanel objects are stored in an internal array called Panels.
Add function:
'------------------------------------------------------
' Sub: add
' Description: Create a new panel
'
Public Function add(ByVal name As String) As ToolPanel
MinimizeAll() ' Make sure they are all minimized
Dim newPanel = New ToolPanel(name, Me)
' Create the panel w/ a ref back to here
Panels.Add(newPanel) ' Add to master array
Dim orderPanels As New ArrayList ' Array of panels
Dim thisPanel As ToolPanel
For Each thisPanel In Panels.ToArray
' Copy the panels to the array
orderPanels.Add(thisPanel)
Next
orderPanels.Reverse()
Me.Controls.Clear()
' Clear out the controls
Me.Controls.Add(newPanel)
' Add the most recent control
For Each thisPanel In orderPanels.ToArray ' Add the Bottom
Me.Controls.Add(thisPanel)
' Addrange isn't working, humm.
Next
Console.WriteLine("Panels: " & orderPanels.Count + 1)
Return newPanel
End Function
What the add function does is:
- Collapse all the panels
- Create a new panel
- Add it to the internal array
- Insert the control and append the reverse list of existing controls into the Controls of the component.
- Returns the new object
Remove Function:
'------------------------------------------------------
' Sub: Remove
' Description: Search for the panel and if found
' remove it and expand the one before it
'
Public Sub Remove(ByVal Title As String)
Dim tmp As ToolPanel
Dim index As Integer
For Each tmp In Panels.ToArray
If tmp.Title.ToLower = Title.ToLower Then
index = Panels.IndexOf(tmp)
Panels.Remove(tmp)
Me.Controls.Remove(tmp)
tmp.Dispose()
If index >= 1 Then
Panels(index - 1).maximize()
End If
End If
Next
End Sub
What the remove function does is:
- Looks at all the panels to find a match
- Removes the panel from the array
- Removes the panel from the control
- Frees up the memory used by the panel
- Maximizes the panel above it (so not to have empty space)
The View function
'------------------------------------------------------
' Sub: View
' Description: Bring this panel in to view
'
Public Sub View(ByVal panel As ToolPanel)
Dim topPanels As New ArrayList
Dim bottomPanels As New ArrayList
Dim middlePanel As ToolPanel
Dim thisControl As ToolPanel
Dim top As Boolean = True
'------------------------------------------------------
' Loop threw the panel array and add the panels to the
' docking arrays.
'
For Each thisControl In Panels.ToArray
If thisControl Is panel Then
middlePanel = thisControl ' Add the middle panel
middlePanel.Maximize()
' Maximize the middle panel and redock
top = False ' Everything after this are bottom panels
Else
If top Then
thisControl.Minimize(DockStyle.Top)
' Minimize and redock
topPanels.Add(thisControl)
' Add the top panels
Else
thisControl.Minimize(DockStyle.Bottom)
' Mimize and redock
bottomPanels.Add(thisControl)
' Add the bottom panels
End If
End If
Next
'------------------------------------------------------
' Add the panels to the control, be careful remember
' order is weird:
'
Me.Controls.Clear() ' Clear out the controls
topPanels.Reverse() ' Reverse the top panel array
Me.Controls.Add(middlePanel) ' Add the middle panel
For Each thisControl In topPanels.ToArray
Me.Controls.Add(thisControl) ' Add the top
Next
For Each thisControl In bottomPanels.ToArray
Me.Controls.Add(thisControl) ' Add the Bottom
Next
End Sub
What the remove function does is:
- Create arrays for the top, middle and bottom panels
- Loops the panels and puts them in there proper arrays
- Clears out the controls
- Adds the activeControl, a reversed version of the top controls and the bottom controls to the control. (Pay attention to the order the controls are added in)
ToolPanel Class
There is nothing real interesting in this class. It's main job is to draw the control and return it to the parent. It also has a bunch of properties and two methods. If you have any questions about this class please feel free to ask.
Public Class ToolPanel : Inherits Panel
Private _Title As String
Private _State As stateInfo = stateInfo.Maximized
Private _Click As stateClick = stateClick.Normal
Private _caption As New Rectangle
Private _client As New Rectangle
Private _captionSize As Integer = 20
Private _parent As Toolbar.Initalize
Sub New(ByVal name As String, ByVal sender As Toolbar.Initalize)
Title = name
_parent = sender
Me.Dock = DockStyle.Fill ' Make new panels filled
Me.DockPadding.Top = _captionSize ' Very important !
End Sub
'------------------------------------------------------
' *** A bunch of properties are here ***
'------------------------------------------------------
'------------------------------------------------------
' Sub: setAreas
' Description: Update the rectangle areas
'
Private Sub setAreas()
_caption = New Rectangle(0, 0, Me.Width, captionHeight)
_client = New Rectangle(0, Me.Top + captionHeight,
Me.Width, Me.Height - captionHeight)
End Sub
'------------------------------------------------------
' Sub: Maximize
' Description: Change the state value and set the
' toolpanel to fill
'
Public Sub Maximize()
Me._State = stateInfo.Maximized
Me.Dock = DockStyle.Fill
End Sub
'------------------------------------------------------
' Sub: Maximize
' Description: Change the state value and set the
' toolpanel to whatever you sent me.
'
Public Sub Minimize(ByVal dock As DockStyle)
Me._State = stateInfo.Minimized
Me.Height = Me.captionHeight
Me.Dock = dock
End Sub
'------------------------------------------------------
' Sub: ToolPanel_MouseDown
' Description: If they clicked in the captionarea then
' change the click state
'
Private Sub ToolPanel_MouseDown(ByVal sender As Object, _
ByVal e As System.Windows.Forms.MouseEventArgs) _
Handles MyBase.MouseDown
If e.Button = MouseButtons.Left Then
If captionArea.IntersectsWith( _
New Rectangle(e.X, e.Y, 1, 1)) Then
Me._Click = stateClick.Clicked
Invalidate(captionArea)
End If
End If
End Sub
'------------------------------------------------------
' Sub: ToolPanel_MouseUp
' Description: If the click state is clicked then call
' the view sub from the parent class
'
Private Sub ToolPanel_MouseUp(ByVal sender As Object, _
ByVal e As System.Windows.Forms.MouseEventArgs) _
Handles MyBase.MouseUp
If e.Button = MouseButtons.Left And _Click = stateClick.Clicked Then
Me._Click = stateClick.Normal
Invalidate(captionArea)
Me._parent.View(Me)
End If
End Sub
'------------------------------------------------------
' Sub: ToolPanel_Paint
' Description: Redraw the controls
'
Private Sub ToolPanel_Paint(ByVal sender As Object, ByVal e As _
System.Windows.Forms.PaintEventArgs) Handles MyBase.Paint
setAreas()
' Get Colors and Pens
'
Dim captionFont As New Font(Me.Font, FontStyle.Regular)
Dim captionBrush As Brush = _
New SolidBrush(ColorTranslator.FromHtml("#ECE9D8"))
Dim captionRect = New Pen(Color.White, 1)
Dim captionPen = New Pen(Color.LightGray, 1)
If _Click = stateClick.Clicked Then
captionBrush = _
New SolidBrush(ColorTranslator.FromHtml("#D8EDEB"))
End If
' Draw it
'
Dim g As Graphics = e.Graphics
g.FillRectangle(captionBrush, captionArea)
g.DrawRectangle(captionRect, captionArea)
g.DrawLine(captionPen, _
New Point(captionArea.X + captionArea.Width - 1, _
captionArea.Y), _
New Point(captionArea.X + captionArea.Width - 1, _
captionArea.Y + captionArea.Height))
g.DrawLine(New Pen(Color.Black, 2), _
New Point(captionArea.X, captionArea.Y + _
captionArea.Height), _
New Point(captionArea.Width, captionArea.Y + _
captionArea.Height))
' Draw Text
'
Dim textSize As SizeF = g.MeasureString(Title, captionFont)
Dim textStart As Integer = (captionHeight / 2) - _
(textSize.Height / 2)
g.DrawString(Title, captionFont, Brushes.Black, Me.Left + 5, _
textStart)
End Sub
End Class
In a nutshell this class
- Gets the information from the parent about what to create
- Sets the dockpadding to the size of the header
- Draws the control and maintains its state information
- Handles mouse clicks and sends them back to the view function of the parent control