Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

A Tab Control Similar to that of Internet Explorer 7

0.00/5 (No votes)
8 Dec 2006 3  
A (fairly) simple tab control with closeable tabs
Sample Image - TabPages.jpg

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 TabColors 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

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