Share via


C#: WinForm TabControl with Add and Close Button

Introduction

If you want to programmatically add Add button and Close buttons to the TabPages of the TabControl on your WinForm (Windows Form) application, which would look something similar to the following:

Then you are in the right article. Follow along and you would have an application with TabControl with Add and Close buttons in no time.

Resources Needed

You can either add your own images as resources for your project or you can use the attached images as your Add and Close buttons. Please note, that the images used here are in .png format.

The Process

Step 1: Have the Add and Close button images as global

Add the full paths of the Add and Close button in your project as global for that class where you are drawing your TabControl as follows:

private string  addButtonFullPath = "Full path for add button goes here";
private string  closeButtonFullPath = "Full path for close button goes here";

Step 2: Set up the DrawMode of the TabControl

Set the DrawMode property of TabControl to OwnerDrawFixed instead of Normal as follows:

Set 3: Drawing the Add and Close buttons on TabControl

Now, go to the DrawItem event on your TabControl and write the following code to draw the Add and Close buttons programmatically:

private void  tabControl_DrawItem(object sender, DrawItemEventArgs e)
{
    try
    {
        var tabPage = this.tabControl.TabPages[e.Index];
        var tabRect = this.tabControl.GetTabRect(e.Index);
        tabRect.Inflate(-2, -2);
        if (e.Index == this.tabControl.TabCount - 1) // Add button to the last TabPage only
        {
            var addImage = new  Bitmap(addButtonFullPath);
            e.Graphics.DrawImage(addImage,
                tabRect.Left + (tabRect.Width - addImage.Width) / 2,
                tabRect.Top + (tabRect.Height - addImage.Height) / 2);
        }
        else // draw Close button to all other TabPages
        {
            var closeImage = new  Bitmap(closeButtonFullPath);
            e.Graphics.DrawImage(closeImage,
                (tabRect.Right - closeImage.Width),
                tabRect.Top + (tabRect.Height - closeImage.Height) / 2);
            TextRenderer.DrawText(e.Graphics, tabPage.Text, tabPage.Font,
                tabRect, tabPage.ForeColor, TextFormatFlags.Left);
        }
    }
    catch(Exception ex) { throw  new Exception(ex.Message); }
}

In order to make the last TabPage, which contains the Add button, resize to a shorter length you need to send a message to the TabControl handle as follows:

using System.Runtime.InteropServices; //  Do not forget this namespace or else DllImport won't work        
 
[DllImport("user32.dll")]
private static  extern IntPtr SendMessage(IntPtr hWnd,  int  msg, IntPtr wp, IntPtr lp);
private const  int TCM_SETMINTABWIDTH = 0x1300 + 49;
private void  tabControl1_HandleCreated(object sender, EventArgs e)
{
    SendMessage(this.tabControl.Handle, TCM_SETMINTABWIDTH, IntPtr.Zero, (IntPtr)16);
}

Step 4: Add action for mouse click event on Add/Close buttons

Now, the exciting part is already here. We need to make something happen when the mouse is clicked on either the Add or Close button. In order to create a new TabPage when the Add button is pressed or close the current TabPage when the Close button is pressed on it, write the following code:

private void  tabControl_SelectedIndexChanged(object sender, EventArgs e)
{
    // If the last TabPage is selected then Create a new TabPage
    if (tabControl.SelectedIndex == tabControl.TabPages.Count -1)
        CreateTabPage();
}

In the SelectedIndexChanged event of the TabControl use the preceding code, so that when the last TabPage on the TabControl is selected, a new TabPage is created instead. Since creating a new TabPage is not within the scope of this article, you can use any method to create TabPages programmatically in the CreateTabPage() method provided above.

After handling the last TabPage, it's time to track mouse click (MouseDown) events on each TabPage to check whether the Close buttons are pressed or not ad execute appropriate actions accordingly.

private void  tabControl_MouseDown(object sender, MouseEventArgs e)
{
    // Process MouseDown event only till (tabControl.TabPages.Count - 1) excluding the last TabPage
    for (var i = 0; i < this.tabControl.TabPages.Count - 1; i++)
    {
        var tabRect = this.tabControl.GetTabRect(i);
        tabRect.Inflate(-2, -2);
        var closeImage = new  Bitmap(closeButtonFullPath);
        var imageRect = new  Rectangle(
            (tabRect.Right - closeImage.Width),
            tabRect.Top + (tabRect.Height - closeImage.Height) / 2,
            closeImage.Width,
            closeImage.Height);
        if (imageRect.Contains(e.Location))
        {
            this.tabControl.TabPages.RemoveAt(i);
            break;
        }
    }
}

In the aforementioned code, you are using the MouseDown event on TabControl and check if the mouse position when clicked falls on the Close button image or not. If it does overlap the Close button image then you remove the TabPage from the TabControl.

And TADA! You have a TabControl which has Add and Close buttons on each TabPages.

Caution: Hot Stuff

Do not forget to set the DrawMode of the TabControl to OwnerDrawFixed or else you won't see the images of Add / Close being drawn on your TabControl and might end up wasting some time just to fix this bug. 

See Also