Click here to Skip to main content
13,597,813 members
Click here to Skip to main content
Add your own
alternative version

Tagged as


57 bookmarked
Posted 25 May 2009
Licenced CPOL

vtTab: Tab Control Extender Class

, 25 May 2009
Rate this:
Please Sign up or sign in to vote.
A class used to extend the visual appearance of the tab control

The Problem with UserControls is...

Some years ago, when I was coding in VB6, a friend asked me to help resolve a problem with a grid control he had written. This was a very elaborate and well written uc, ran without issues, except.. when the OS was an eastern European language version. For reasons that I don't think were ever discovered, it would freeze the application at random intervals, (cross threading? subclassing?). The point is that you can spend months writing the perfect user control, really put your heart into it, with the most pristine and elegant code behind it, but you just never know...

Another problem is that usercontrols like the one mentioned take a lot of time to write and perfect. I have written a grid that took two months and 27 thousand lines of code to complete, it is simply too much work to be practical.

This is why I started skinning native controls, it makes the most sense. Many of the controls in your OS have been around for years and have withstood the test of time. The usercontrols packaged with Visual Studio are implementations of these MFC control classes, neatly packaged and presented for use within the IDE. There is a problem however and that is...

The Good, the Bad, and the VS Dev Team

I would really hate to be one of the guys who has to read their 'fan mail', my heart goes out to them, really. Just about every usercontrol in Visual Studio either has a bug, is crippled in some way, or its appearance is sub-par. The Tab Control is a good example: There is no visual style if the control is vertically aligned, but worse than that, if the tab headers are aligned to the bottom of the control, the last two pixels are cut off (in Vista any ways).

How is it, that after all these versions of Visual Studio, the same annoying bugs are still there!? I mean, surely they know about it by now.

Well, I have a theory about that. Microsoft seems to often work towards opposing goals, they want better security, but they want a high level of automation, they want technology to evolve, but they crush competing ideas, etc. I believe this is one of those instances. They want us all to write applications with Visual Studio, (and so more software ported to Windows), but they don't want you to write better competing software. This has been going on forever, API that is not documented, (some calls in ntdll have actually been named only with numbers), limiting access to features within libraries, and of course, sticking us with crippled, buggy usercontrols.

And Now for Something Completely Different

Ok, now that I have that out of my system, (and so long as this article doesn't 'vanish', my IP isn't blackholed, and my electricity stays on, gulp), let's forge ahead..

I started writing a skinning library about a month ago (I'll release it next month), of which the cTabControl is a member class. My hope is that a single library will be able to customize the appearance of most of the common controls, doing away with the need for some usercontrols, and shortening the development cycle.

Why Use Bitmaps?

The main reason is that blitting an image to a dc is far faster and consumes fewer resources than constantly recreating the background via gradients or fills. Another good reason is versatility, many styles already exist on popular skinning sites such as You can choose a visual style that would work well with your application, and (with permission), extract the desired bitmaps and port them to the control. But, for the sake of completeness, I also added a custom drawing style to the class.

Using a Paint Struct to Control Painting

The BeginPaint/EndPaint API allow for a precise control of the painting process. Without these calls, you would only be overpainting the control, causing a lot of flicker. The ValidateRect call tells the operating system that no further painting is required in this area.

switch (m.Msg)
case WM_PAINT:
if (!_bPainting)
_bPainting = true;
// start painting engine
BeginPaint(m.HWnd, ref pntStrct);
ValidateRect(m.HWnd, ref pntStrct.rcPaint);
// done
EndPaint(m.HWnd, ref pntStrct);
_bPainting = false;
base.WndProc(ref m);

Using SendMessage to Get the Tab Header Size

There just doesn't seem to be another way to do this. With all those control fields available, and nobody thought of slipping this in?

SendMessage(_tabControlWnd, TCM_GETITEMRECT, i, ref tabRect);

headerRect = new Rectangle(tabRect.Left, tabRect.Top, 
		tabRect.Right - tabRect.Left, tabRect.Bottom - tabRect.Top);

Some Graphics Code

One of the problems with using images is that they don't always respond well to stretching, particularly the borders. You can solve this by drawing the borders separately, and stretching them on the bias as border elements.

// to maintain a constant border depth while stretching the bitmap //
if (align == TabAlignment.Bottom || align == TabAlignment.Top)
bm = new Bitmap(tabRect.Right - tabRect.Left, xsize);
bm = new Bitmap(xsize, tabRect.Right - tabRect.Left);
Graphics gcl = Graphics.FromImage(bm);
// clone the inner portion
cl = _tabHeaderBitmap.Clone(new Rectangle((state * width) + 2, 2, 
	width - 4, _tabHeaderBitmap.Height - 2), 
// draw to new bmp
if (align == TabAlignment.Bottom || align == TabAlignment.Top)
gcl.DrawImage(cl, new Rectangle(2, 2, tabRect.Right - tabRect.Left, xsize));
gcl.DrawImage(cl, new Rectangle(2, 2, xsize, tabRect.Right - tabRect.Left));
// clone and draw the edges
// left
cl = _tabHeaderBitmap.Clone(new Rectangle(state * width, 0, 2, 
	_tabHeaderBitmap.Height), System.Drawing.Imaging.PixelFormat.DontCare);
gcl.DrawImage(cl, new Rectangle(0, 0, 2, xsize));
// top
cl = _tabHeaderBitmap.Clone(new Rectangle(state * width + 2, 0, 
	width - 4, 2), System.Drawing.Imaging.PixelFormat.DontCare);
gcl.DrawImage(cl, new Rectangle(2, 0, bm.Width - 4, 2));
cl = _tabHeaderBitmap.Clone(new Rectangle((state * width) + 
	(width - 2), 0, 2, _tabHeaderBitmap.Height), 
gcl.DrawImage(cl, new Rectangle(bm.Width - 2, 0, 2, xsize));

How to Rotate a Bitmap

Sounds easy, right... but during my research I found answers varying from a complete redraw with SetPixel, to using a Matrix to transform the image.

case TabAlignment.Bottom:
case TabAlignment.Left:


Creating the Bitmaps

This is very straightforward if you are even casually familiar with an image processor. I used Fireworks and copied the images from screenshots. Below is the layout required:


Style Examples

Bitmap Styles

Included are two examples of using a bitmap, an Internet Explorer 7, and a Vista like look:


Custom Drawn

I also included a Vista style and a gradient in the custom drawn example.


The class also includes an upgrade to the ToolTip with an internal tip class.



  • 25th May, 2009: Initial post


This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


About the Author

John Underhill
Network Administrator
Canada Canada
Network and programming specialist. Started in C, and have learned about 14 languages since then. Cisco programmer, and lately writing a lot of C# and WPF code, (learning Java too). If I can dream it up, I can probably put it to code. My software company, (VTDev), is on the verge of releasing a couple of very cool things.. keep you posted.

You may also be interested in...


Comments and Discussions

GeneralIdea of improvement Pin
claudetom016-Jun-09 2:22
memberclaudetom016-Jun-09 2:22 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

Permalink | Advertise | Privacy | Cookies | Terms of Use | Mobile
Web01-2016 | 2.8.180621.3 | Last Updated 25 May 2009
Article Copyright 2009 by John Underhill
Everything else Copyright © CodeProject, 1999-2018
Layout: fixed | fluid