The Problem
Now, Microsoft says right off for the BackColor property that "This member is not meaningful for this control." Why? Apparently they don't want developers monkeying with standard forms where the user should be able to change their Window's appearance through control panel.
Fair enough, but sometimes the app needs to look different. For instance, for our most recent update we have a background image; having the battleship gray across it doesn't look so pretty. The more annoying part is that in the IDE, the form looks perfect. I can't tell you how annoying it is that Microsoft knows what we want, because they put it right in the IDE, but when it's running, they say we can't have it. (Maybe they'll get it right for Windows 7.) ;)
Now the easiest solution is to allow an override for BackColor and BackgroundImage in the TabControl, but that would require Microsoft to realize the error of their ways. Instead, we will have to code around their "behavior by design". ;)
The Research
Initially, I found this post on setting the DrawMode to OwnerDrawFixed (I wonder if that means they admit the other is broken ;) ) It requires you to attach to the DrawItem event. This did lead me down the right path, but it only changed the tabs and then only their color.
The next post I found had a comment by LauraM that showed me how to change the extended part of the tab strip: the strip to the right of the tabs. This got me closer, but what I really needed to do was get the image from the parent and bring it through.
At this point it was all up to me. What I needed was more complex than what it appeared everyone else needed: I needed to have the underlying image or background color show through, depending on which was on the parent control, and if there were images on the tabs I needed those to show through as well. All this was in addition to setting the background color of the tabs to begin with.
The Solution
I started with the code that these posts brought me and altered it to make a more versatile setup. For this setup, there are three initial steps:
- Change the DrawMode property to OwnerDrawFixed.
- Attach to the DrawItem event. With the versatility of the method we will create, you will be able to use it for each tab control on the form.
- Create the method signatures.
(At this point if you just want code the entire method is at the bottom.)
Note that as soon as you change the DrawMode, the perfect TabControl you had in the IDE now becomes completely blank. It appears you can either have it work right in the IDE or in run mode, but not both. It's the kind of thrilling craziness that makes you glad you signed up for this career. ;)

The Code
As we want this to work for all our TabControls, we want two signatures: one that allows us to pass in an associated ImageList as needed, and one that doesn't require one. The one with fewer parameters will simply call the other with a null value:
/// <summary>Draws the specified tab control.</summary>
/// <param name="tabControl">The control on which to draw.</param>
/// <param name="e">The event arguments.</param>
private void DrawTabControlTabs(TabControl tabControl, DrawItemEventArgs e)
{
DrawTabControlTabs(tabControl, e, null);
}
/// <summary>Draws the specified tab control.</summary>
/// <param name="tabControl">The control on which to draw.</param>
/// <param name="e">The event arguments.</param>
/// <param name="images">The images for the tab control.</param>
private void DrawTabControlTabs(TabControl tabControl, DrawItemEventArgs e, ImageList images)
{
...
}
Now each of the TabControls that use this must have the DrawItem event filled out. These events do nothing more than call DrawTabControlTabs, passing the TabControl to draw, and the DrawItemEventArgs parameter that was passed to the event. Those that have icons, need to pass a third parameter of an ImageList.
/// <summary>Occurs when the items is drawn.</summary>
/// <param name="sender">The object that fired the event.
/// <param name="e">The arguments.</param>
private void tabSections_DrawItem(object sender, DrawItemEventArgs e)
{
DrawTabControlTabs(tabSections, e, this.icons);
}
Now, luckily, with everything that's going on it all fits in the one method. It's a bit longer than I typically like in a method (I love the nice short methods), but it's reasonable. The method consists of six basic steps.
Step 1: Get the bounding rectangle for the end of the tab strip
// Get the bounding end of tab strip rectangles.
Rectangle tabstripEndRect = tabControl.GetTabRect(tabControl.TabPages.Count - 1);
RectangleF tabstripEndRectF = new RectangleF(tabstripEndRect.X + tabstripEndRect.Width, tabstripEndRect.Y - 5,
tabControl.Width - (tabstripEndRect.X + tabstripEndRect.Width), tabstripEndRect.Height + 5);
Step 2: Fill the end using the parent's background image or color
Note that for using the background image we need to get the source coordinates on the parent control or form so we can pull the part of the image precisely under the area we want to draw it.
// First, do the end of the tab strip.
// If we have an image use it.
if (tabControl.Parent.BackgroundImage != null)
{
RectangleF src = new RectangleF(tabstripEndRectF.X + tabControl.Left, tabstripEndRectF.Y + tabControl.Top, tabstripEndRectF.Width, tabstripEndRectF.Height);
e.Graphics.DrawImage(tabControl.Parent.BackgroundImage, tabstripEndRectF, src, GraphicsUnit.Pixel);
}
// If we have no image, use the background color.
else
{
using (Brush backBrush = new SolidBrush(tabControl.Parent.BackColor))
{
e.Graphics.FillRectangle(backBrush, tabstripEndRectF);
}
}
Step 3: Pull the tab's color and name and apply the background color to the tab
Now it's worth noting at this point that the DrawItem event is fired for the drawing of each TabPage in the TabControl. Since this is a UI method (typically where the speed bottleneck is the user) it's not of great concern that in the previous steps we are drawing the parts of the TabControl that only need to be drawn once per control as many times as we have tabs. I did not see a speed issue with this and storing state may get a little nasty so I am leaving it. You may wish to do something differently. The code from this step on is for the tabs themselves.
// Set up the page and the various pieces.
TabPage page = tabControl.TabPages[e.Index];
Brush BackBrush = new SolidBrush(page.BackColor);
Brush ForeBrush = new SolidBrush(page.ForeColor);
string TabName = page.Text;
// Set up the offset for an icon, the bounding rectangle and image size and then fill the background.
int iconOffset = 0;
Rectangle tabBackgroundRect = e.Bounds;
e.Graphics.FillRectangle(BackBrush, tabBackgroundRect);
Step 4: Put any icons that exist on the tabs
Now this works whether you used an index or a key, but make sure you pass an ImageList to the method or this code won't get called.
// If we have images, process them.
if (images != null)
{
// Get sice and image.
Size size = images.ImageSize;
Image icon = null;
if (page.ImageIndex > -1)
icon = images.Images[page.ImageIndex];
else if (page.ImageKey != "")
icon = images.Images[page.ImageKey];
// If there is an image, use it.
if (icon != null)
{
Point startPoint = new Point(tabBackgroundRect.X + 2 + ((tabBackgroundRect.Height - size.Height) / 2),
tabBackgroundRect.Y + 2 + ((tabBackgroundRect.Height - size.Height) / 2));
e.Graphics.DrawImage(icon, new Rectangle(startPoint, size));
iconOffset = size.Width + 4;
}
}
Step 5: Spit out the text
// Draw out the label.
Rectangle labelRect = new Rectangle(tabBackgroundRect.X + iconOffset, tabBackgroundRect.Y + 3,
tabBackgroundRect.Width - iconOffset, tabBackgroundRect.Height - 3);
StringFormat sf = new StringFormat();
sf.Alignment = StringAlignment.Center;
e.Graphics.DrawString(TabName, e.Font, ForeBrush, labelRect, sf);
Step 6: Clean-up
//Dispose objects
sf.Dispose();
BackBrush.Dispose();
ForeBrush.Dispose();
The Fixed Form
Now here is the fixed form with the background image pulled through (and on other tabs the background color). Also icons show where appropriate.

The Method
Here is the full method. It's annoying as hell that we have to code this to make these things work, but there's not much to it. Happily this is fairly extensible supporting background image or backcolor bleed through and images by key or index. Hopefully if you were in the same boat I was, you will find this code helpful to the point you can quickly get on with the important parts of coding your project and not the workarounds.
private void DrawTabControlTabs(TabControl tabControl, DrawItemEventArgs e, ImageList images)
{
// Get the bounding end of tab strip rectangles.
Rectangle tabstripEndRect = tabControl.GetTabRect(tabControl.TabPages.Count - 1);
RectangleF tabstripEndRectF = new RectangleF(tabstripEndRect.X + tabstripEndRect.Width, tabstripEndRect.Y - 5,
tabControl.Width - (tabstripEndRect.X + tabstripEndRect.Width), tabstripEndRect.Height + 5);
// First, do the end of the tab strip.
// If we have an image use it.
if (tabControl.Parent.BackgroundImage != null)
{
RectangleF src = new RectangleF(tabstripEndRectF.X + tabControl.Left, tabstripEndRectF.Y + tabControl.Top, tabstripEndRectF.Width, tabstripEndRectF.Height);
e.Graphics.DrawImage(tabControl.Parent.BackgroundImage, tabstripEndRectF, src, GraphicsUnit.Pixel);
}
// If we have no image, use the background color.
else
{
using (Brush backBrush = new SolidBrush(tabControl.Parent.BackColor))
{
e.Graphics.FillRectangle(backBrush, tabstripEndRectF);
}
}
// Set up the page and the various pieces.
TabPage page = tabControl.TabPages[e.Index];
Brush BackBrush = new SolidBrush(page.BackColor);
Brush ForeBrush = new SolidBrush(page.ForeColor);
string TabName = page.Text;
// Set up the offset for an icon, the bounding rectangle and image size and then fill the background.
int iconOffset = 0;
Rectangle tabBackgroundRect = e.Bounds;
e.Graphics.FillRectangle(BackBrush, tabBackgroundRect);
// If we have images, process them.
if (images != null)
{
// Get sice and image.
Size size = images.ImageSize;
Image icon = null;
if (page.ImageIndex > -1)
icon = images.Images[page.ImageIndex];
else if (page.ImageKey != "")
icon = images.Images[page.ImageKey];
// If there is an image, use it.
if (icon != null)
{
Point startPoint = new Point(tabBackgroundRect.X + 2 + ((tabBackgroundRect.Height - size.Height) / 2),
tabBackgroundRect.Y + 2 + ((tabBackgroundRect.Height - size.Height) / 2));
e.Graphics.DrawImage(icon, new Rectangle(startPoint, size));
iconOffset = size.Width + 4;
}
}
// Draw out the label.
Rectangle labelRect = new Rectangle(tabBackgroundRect.X + iconOffset, tabBackgroundRect.Y + 3,
tabBackgroundRect.Width - iconOffset, tabBackgroundRect.Height - 3);
StringFormat sf = new StringFormat();
sf.Alignment = StringAlignment.Center;
e.Graphics.DrawString(TabName, e.Font, ForeBrush, labelRect, sf);
//Dispose objects
sf.Dispose();
BackBrush.Dispose();
ForeBrush.Dispose();
}








