Introduction
Recently, several of my projects have required the ability to handle multiple documents. I figured IE7's tabs were a pretty good way of handling this, and here is the control that resulted.
Overview
This control is basically a managed collection of pages (or controls). It is event driven, indicating when a page has been added, removed, selected, or closed. The close event is cancellable, allowing you to prevent pages from being closed unless certain criteria are met.
I should probably add that there is no support for adding tabs via the IDE. I'd considered doing this, but it didn't really make sense. All of my projects are dealing with documents which will be opened and closed at runtime, and so I designed this control with that in mind.
Using the Control
Creating the Control
To create the control, add a reference to TabPages.dll, then create the TabPages.PageCollection
control either programmatically or through the IDE.
Adding Pages
To add a page, call the Add
method. This method takes a TabPage
object as a parameter. The TabPage
object has text, a control (the control you wish to display when the tab is selected), and an optional toolTip
string which will be displayed when the mouse hovers over the tab.
Removing Pages
To remove a page, call the Remove
, RemoveAt
, or Clear
methods. These methods correspond to IList(Of TabPage)
methods.
You can also call the Close
method of the TabPage
you wish to close (this will raise the PageClosing
event, whereas the Remove
methods will not).
Events
There are several events which will be of interest to users of the PageCollection control, and they are defined as follows:
Public Event PageAdded(ByVal page As TabPage)
Public Event PageRemoved(ByVal page As TabPage)
Public Event CurrentPageChanged_
(ByVal currentPage As TabPage, ByVal previousPage As TabPage)
Public Event PageClosing(ByVal page As TabPage, ByRef cancel As Boolean)
The PageAdded
event fires when a page is added to the collection.
The PageRemoved
event fires when a page is removed from the collection.
The CurrentPageChanged
event fires when the currently selected page changes (when a different tab becomes the active tab). This event gets passed the currently selected tab as well as the previously selected tab.
The PageClosing
event fires when a page is attempting to close. This gets fired when the user clicks close, or when the TabPage
's Close
method is called. This does not fire when Remove
, RemoveAt
, or Clear
are called. The page
parameter indicates which page is attempting to close, and the cancel
parameter allows cancellation of the close. If the close is not cancelled, the page will be removed from the collection and the PageRemoved
event will fire.
Appearance
The appearance of this control could be more flexible, but for now, it is adequate. There are two properties of interest when customizing the PageCollection
control's appearance.
The TabColor
property specifies the color which will be used to generate the control's theme. All gradients, borders, and menu colors are generated from this color. Play around with it and see what you like. My favorite TabColor
s are LightGray
and LightSteelBlue
.
The TopMargin
property specifies the difference between the height of the selected tab and the non selected-tabs.
Points Of Interest
As I said earlier, this control is pretty simple. But there were two semi-challenges while developing it.
Flickering
The initial version flickered something awful, even though I was double buffering. I was able to get around this by passing the WM_SETREDRAW
message to the SendMessage
API method. It's now smooth as... er... Bono or some similarly smooth person.
Private Const WM_SETREDRAW As Integer = &HB
Private Declare Auto Function SendMessage Lib "User32" Alias "SendMessage" _
(ByVal hWnd As IntPtr, ByVal msg As Integer, _
ByVal wParam As Integer, ByVal lParam As Integer) As Boolean
What was happening is each time I resized (or added/removed controls), I would regenerate the list of displayed tab controls. Each time the list was regenerated, each tab would invalidate. It was ugly. WM_SETREDRAW
basically tells a handle (control) that you don't want it to paint any more until you say so. For more information on this, see the FlickerFreeControl
in the project.
Menu Theme
The other thing I wanted was menus which matched the color scheme of the tabs. To accomplish this, I inherited from the ToolStripRenderer
. I'd never used this before, but what it allows you to do is customize the painting of a toolstrip
object (in this case, my context menu). See the DropDownRenderer
class in the source for the implementation.
Conclusion
I'm not going to repeat myself. I hope you like this control. If you make significant improvements to it, please send them my way so I can capitalize on them. Peace.
History
- 8th December, 2006: Initial post