Click here to Skip to main content
15,868,016 members
Articles / Desktop Programming / Windows Forms
Article

TabStrip Control

Rate me:
Please Sign up or sign in to vote.
4.96/5 (85 votes)
30 May 2006CPOL3 min read 274.4K   10.7K   295   35
A flexible TabStrip control with basic design-time support.

Sample Image - tabstrips_demo.png

Introduction

With the introduction of .NET Framework 2.0, we have got a set of Strip controls, like MenuStrip and ToolStrip. They have rich design-time and run-time features and flexible interfaces. So the idea was to create a ToolStrip-like tab control supporting formatting in tab headers, animated icons, tab headers at any side, run-time tab reordering, tab grouping, and other stuff we miss in the Windows Forms TabControl.

Background

To implement the TabStrip and TabStripButton classes, we should use ToolStrip and ToolStripButton. Drawing should be performed via a ToolStripRenderer derived class - it's more flexible than painting in OnPaint. To use the current Windows XP theme, we ought to use methods from the System.Windows.Forms.VisualStyles namespace. The framework already wraps the uxTheme.dll, so we can go without a lot of P/Invoke calls.

We have to provide at least two ways for painting the interface: using visual styles, and without using them (we cannot use uxTheme on systems that don't support themes). So, let's add a theming check:

C#
private bool useVS = Application.RenderWithVisualStyles;

/// <summary>
/// Returns if visual styles should be applied for drawing
/// <summary>
public bool UseVS
{
    get { return useVS; }
    set 
    {
        if (value && !Application.RenderWithVisualStyles)
            return;
        useVS = value; 
    }
}

ToolStrip generally has a few RenderModes: System, Professional, etc. So, if we want to paint an interface using any of them, we can either make derived classes from each Renderer class, or make one ToolStripRenderer derived class and use instances of the needed type within it. It will look like this:

C#
private ToolStripRenderer currentRenderer = null;
private ToolStripRenderMode renderMode = ToolStripRenderMode.Custom;

/// <summary>
/// Gets or sets render mode for this renderer
/// </summary>
public ToolStripRenderMode RenderMode
{
    get { return renderMode; }
    set
    {
        renderMode = value;
        switch (renderMode)
        {
            case ToolStripRenderMode.Professional:
                currentRenderer = new ToolStripProfessionalRenderer();
                break;
            case ToolStripRenderMode.System:
                currentRenderer = new ToolStripSystemRenderer();
                break;
            default:
                currentRenderer = null;
                break;
        }
    }
}

Now, our toolbar's behavior should depend on its orientation. For this, we can use the ToolStrip.Orientation property. But it solves only one half of the problem. We still don't know at which side our TabStrip is docked. Theoretically, we can refer to the Dock and Parent properties to determine this, but in most cases, we know where our bar is located. So, we'll only make a property which determines if tab headers should be flipped.

C#
private bool mirrored = false;

/// <summary>
/// Gets or sets whether to mirror background
/// </summary>
/// <remarks>Use false for left and top positions,
/// true for right and bottom</remarks>
public bool Mirrored
{
    get { return mirrored; }
    set { mirrored = value; }
}

The rest of the TabStripRenderer class is code for drawing, and nothing more interesting.

So we can go straight to the TabStripButton class. It extends the ToolStripButton with some new properties:

  • HotTextColor - Text color when mouse hovers TabStripButton.
  • SelectedTextColor - Text color when TabStripButton is SelectedTab.
  • SelectedFont - Font when TabStripButton is SelectedTab.
  • IsSelected - Gets or sets if tab is selected.

Also, we have to shadow the Checked, Padding, and Margin properties to avoid the user from breaking the pretty interface. These properties are also removed from the designer properties window.

Now, about the TabStrip class. It provides some new properties, most of them are wrappers for properties of the internal TabStripRenderer class.

  • UseVisualStyles - specifies if the system visual styles should be applied. If system does not support themes, this property is ignored, and the painting goes the custom way.
  • FlipButtons - specifies if the buttons should be drawn flipped. Set this to true if TabStrip should display tabs on the right or bottom side.
  • RenderStyle - property to use instead of RenderMode. Renderer and RenderMode are shadowed (for normal behavior), and removed from the designer property window.

Design-time support

First of all, we need to register TabStripButton as an available control for ToolStrip:

C#
[ToolStripItemDesignerAvailability(ToolStripItemDesignerAvailability.ToolStrip)]
public class TabStripButton : ToolStripButton

Note that the class should be declared as public.

Designer view 1

Now, let's add the "Insert tab page" option in TabStrip's designer menu:

C#
DesignerVerb insPage = null;

protected void InitControl()
{
    // ...
    // other initialization here
    // ...
    insPage = new DesignerVerb("Insert tab page", 
              new EventHandler(OnInsertPageClicked));
}

public override ISite Site
{
    get
    {
        ISite site = base.Site;
        if (site != null && site.DesignMode)
        {
            IContainer comp = site.Container;
            if (comp != null)
            {
                IDesignerHost host = comp as IDesignerHost;
                if (host != null)
                {
                    IDesigner designer = 
                       host.GetDesigner(site.Component);
                    if (designer != null && 
                          !designer.Verbs.Contains(insPage))
                        designer.Verbs.Add(insPage);
                }
            }
        }
        return site;
    }
    set
    {
        base.Site = value;
    }
}

protected void OnInsertPageClicked(object sender, EventArgs e)
{
    ISite site = base.Site;
    if (site != null && site.DesignMode)
    {
        IContainer container = site.Container;
        if (container != null)
        {
            TabStripButton btn = new TabStripButton();
            container.Add(btn);
            btn.Text = btn.Name;
        }
    }
}

The result looks like this:

Designer view 2

Summary

So, we have a new flexible control now. Unlike TabControl, it's not a container control, but it can be combined with other controls to simulate a container. And when tabs are needed for navigation, it leaves TabContol in the dust. And visual styles and rich picture support will make your application more cute and friendly.

History

  • 30.05.2006 - The very first version.

License

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


Written By
Web Developer
Russian Federation Russian Federation
Alex
.NET Developer
Russian Federation
rakemaker@gmail.com

Comments and Discussions

 
GeneralMy vote of 5 Pin
whar27-Feb-12 8:37
whar27-Feb-12 8:37 
QuestionGood job Pin
Mike Hankey29-Aug-11 12:31
mveMike Hankey29-Aug-11 12:31 
GeneralImprovement Pin
DFDotNet2-Dec-09 23:26
DFDotNet2-Dec-09 23:26 
GeneralC++ version Pin
Super Garrison7-Jul-09 8:32
Super Garrison7-Jul-09 8:32 
GeneralRe: C++ version Pin
Adrian Andrei24-Jan-15 4:12
Adrian Andrei24-Jan-15 4:12 
GeneralLicensing Pin
Lutosław1-Jul-09 4:53
Lutosław1-Jul-09 4:53 
Generalnice code, but has problem when I and a dropdownbutton Pin
chtoph10-May-09 17:04
chtoph10-May-09 17:04 
GeneralThanks a million Pin
Jayson Ragasa22-Mar-09 20:11
Jayson Ragasa22-Mar-09 20:11 
GeneralThanks a million Pin
Pankaj Nikam9-Feb-09 19:46
professionalPankaj Nikam9-Feb-09 19:46 
GeneralNeed some help Pin
imresoft1-Jul-08 4:22
imresoft1-Jul-08 4:22 
System.ComponentModel.Design.Serialization.CodeDomSerializerException: Could not find type 'Messir.Windows.Forms.TabStrip'. Please make sure that the assembly that contains this type is referenced.

Can you help me? The project seems excellent, but I can't use that without the type Messir.Windows.Forms.TabStrip.

Thank you!
QuestionBeautiful... someone can write the same in vb.net ? Pin
wakashich29-Mar-08 22:09
wakashich29-Mar-08 22:09 
AnswerRe: Beautiful... someone can write the same in vb.net ? Pin
The Cake of Deceit25-Jun-08 10:38
The Cake of Deceit25-Jun-08 10:38 
GeneralClose button Pin
Giorgi Dalakishvili26-Dec-07 23:57
mentorGiorgi Dalakishvili26-Dec-07 23:57 
GeneralRe: Close button Pin
The Cake of Deceit7-May-08 0:22
The Cake of Deceit7-May-08 0:22 
GeneralCtrl Tab Pin
Chris_McGrath23-Oct-07 18:56
Chris_McGrath23-Oct-07 18:56 
Generalearth to Alex Pin
BillWoodruff5-May-07 9:14
professionalBillWoodruff5-May-07 9:14 
GeneralTabStrip.Designer.cs not found in project Pin
Andrey Kaplun23-Apr-07 9:27
Andrey Kaplun23-Apr-07 9:27 
General'Messir.Windows.Forms.TabStrip' assembly Pin
Geert van Horrik22-Feb-07 5:50
Geert van Horrik22-Feb-07 5:50 
GeneralRe: 'Messir.Windows.Forms.TabStrip' assembly Pin
Geert van Horrik24-Feb-07 3:19
Geert van Horrik24-Feb-07 3:19 
GeneralDamn good code. [modified] Pin
dethtroll15-Feb-07 0:19
dethtroll15-Feb-07 0:19 
GeneralLicensing Pin
Mattman20629-Jan-07 2:58
Mattman20629-Jan-07 2:58 
QuestionBug? Pin
karrphoto11-Jan-07 19:34
karrphoto11-Jan-07 19:34 
AnswerRe: Bug? Pin
VolcanicZone9-Oct-07 23:51
VolcanicZone9-Oct-07 23:51 
Questioncombination with container control Pin
maorray11-Jan-07 3:10
maorray11-Jan-07 3:10 
GeneralGreat Pin
Tinus Becker16-Aug-06 1:12
Tinus Becker16-Aug-06 1:12 

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.