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.
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.
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.
To remove a page, call the
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).
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)
PageAdded event fires when a page is added to the collection.
PageRemoved event fires when a page is removed from the collection.
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.
PageClosing event fires when a page is attempting to close. This gets fired when the user clicks close, or when the
Close method is called. This does not fire when
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.
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.
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
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.
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.
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.
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.
- 8th December, 2006: Initial post