35

is there anyone can tell me how to add close button in each tab in using tabControl in C#? i plan to use button pic for replacing [x] in my tab..

thank you

KMån
  • 9,896
  • 2
  • 31
  • 41
user380899
  • 351
  • 1
  • 3
  • 3

4 Answers4

62

Without deriving a class, here is a neat snippet: http://www.dotnetthoughts.net/implementing-close-button-in-tab-pages/

Set the DrawMode property of the Tab Control to OwnerDrawFixed. This property decides whether system or developer can paint the captions. Add the code in the DrawItem event of the Tab Control – This event will be invoked for painting each Tab Page.

    //This code will render a "x" mark at the end of the Tab caption. 
e.Graphics.DrawString("x", e.Font, Brushes.Black, e.Bounds.Right - 15, e.Bounds.Top + 4);
e.Graphics.DrawString(this.tabControl1.TabPages[e.Index].Text, e.Font, Brushes.Black, e.Bounds.Left + 12, e.Bounds.Top + 4);
e.DrawFocusRectangle();

Now for close button action, we need to add the following code to the MouseDown event of the Tab Control.

//Looping through the controls.
for (int i = 0; i < this.tabControl1.TabPages.Count; i++)
{
    Rectangle r = tabControl1.GetTabRect(i);
   //Getting the position of the "x" mark.
    Rectangle closeButton = new Rectangle(r.Right - 15, r.Top + 4, 9, 7);
    if (closeButton.Contains(e.Location))
    {
        if (MessageBox.Show("Would you like to Close this Tab?", "Confirm", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes)
        {
            this.tabControl1.TabPages.RemoveAt(i);
            break;
        }
    }
}
Pvria Ansari
  • 406
  • 4
  • 20
andycted
  • 812
  • 6
  • 13
  • 5
    This deserves more votes. It really helped me. Side note: Use `tabControlBottom.SizeMode = TabSizeMode.Fixed;` and `tabControlBottom.ItemSize` to set the tab width and height. – Toon Casteele Apr 18 '13 at 11:28
  • 1
    Not sure if the code is buggy or i'm just doing it wrong. I'm using the code exactly like this (cut and paste, even!), the individual tabs do not take into account the additional real estate for the "x" button and as such, the tab is only actually large enough to hold the text (the last letter of the text and the 'x' overlay each other) – Beta033 Jan 13 '16 at 18:13
  • 3
    Add a few blank spaces to the end of each string that you pass as a name for the tab. That will always reserve that much space for the 'x'. – Karlovsky120 Jan 15 '16 at 23:03
  • 1
    Could you guys explain why in the mouse click event I need to iterate through all the tabpages, when I can use .selectedIndex and .selectedTab to find the current one? See my answer http://stackoverflow.com/questions/3183352/close-button-in-tabcontrol/36070187#36070187 – Redoman Mar 17 '16 at 19:44
  • 3
    Actually you should use MouseUp event, then users can cancel the close operation by moving the mouse away before releasing. This is also how brosers and other tab programs do. – Poul Bak Apr 15 '18 at 15:46
  • Would be nice to have the full code, No idea how to create the event handler or where to put the first section of code. – user2924019 Mar 03 '19 at 20:45
  • Is it possible to change the look of OwnerDrawFixed Mode? It looks kind of old-style Windows to me.. – Momoro Jan 31 '20 at 07:38
  • This is not showing right for long content in the tab how do i fix this – c-sharp-and-swiftui-devni Jul 12 '21 at 16:24
8

Adding to the other answers... why iterating through all the tabs on mouse click event when we can just detect the current tab with .SelectedIndex and .SelectedTab ?

Like so:

private void tabControl1_MouseDown(object sender, MouseEventArgs e)
{
    Rectangle r = tabControl1.GetTabRect(this.tabControl1.SelectedIndex);
    Rectangle closeButton = new Rectangle(r.Right - 15, r.Top + 4, 9, 7);
    if (closeButton.Contains(e.Location))
    {
        this.tabControl1.TabPages.Remove(this.tabControl1.SelectedTab); 
    }
}

What seems to happen is, the moment you click on a tabPage for closing it, it also gets selected, thus allowing the close button to close the right tabPage. To me it works but please take this with extra care though as I am not completely sure of possible drawbacks (my initial sentence wasn't a completely rhetorical question since I'm kinda new to .Net...).

johnny 5
  • 19,893
  • 50
  • 121
  • 195
Redoman
  • 3,059
  • 3
  • 34
  • 62
  • A small problem with this code is that when you have many tabs opened and you have them on multiple rows of tabs, if you try to close a tab on a different row than the currently active row of tabs, then this code will only make active the row of tabs containing the tab we want to close and select that tab, but not close it. I'm just reporting this, don't have time to inspect it further now, but I feel the solution must not be so so hard. – Redoman Mar 22 '16 at 14:14
  • Haven't tested this yet, but i see what may possibly be an issue. If you have multiple tabs open, (Tabs A, B, and C). If you are working in tab C and decide you want to close tab A without selecting (opening) it. Would clicking the closing X in tab A not close tab C as it is closing the selected tab? – DDuffy Dec 28 '16 at 09:17
  • @DDuffy no, it would not close the wrong tab in my app. In my case the moment you click on the X you also select the tab, so the correct one will be closed. – Redoman Dec 29 '16 at 10:10
  • 1
    Cool beans. I like the conciseness (is that a word?) of your answer. Not a big fan of chunky code. You, my good man, have earned yourself a +1. – DDuffy Dec 29 '16 at 12:04
  • Why is it that when I press the `closebutton`, it deletes the tab I want to delete, but also deletes the next tab in front of it? Could anyone help? – Momoro Feb 03 '20 at 09:17
  • @Momoro it's probably related to the type of mouse event you're using. Are you using a MouseDown event rather than some other type of mouse event? – Redoman Feb 06 '20 at 21:20
  • I fixed it :) Turns out I was using **MouseClick**, which was a fatal error.. Thanks for pointing me in the right direction! – Momoro Feb 07 '20 at 06:52
  • Is there a way to remove the _Windows XP_ UI, but keep the close button? The `TabControl` is ugly when `OwnerDrawFixed` is set to true. I heard you can use `VisualStyles`, but I don't know how. – Momoro May 06 '20 at 20:32
2

Try this code:

    private Point _imageLocation = new Point(13, 5);
        private Point _imgHitArea = new Point(13, 2);

        private void Form1_Load(object sender, EventArgs e)
        {
            tabControl1.DrawMode = TabDrawMode.OwnerDrawFixed;
    tabControl1.DrawItem += tabControl1_DrawItem;
    CloseImage = WindowsFormsApplication3.Properties.Resources.closeR;
    tabControl1.Padding = new Point(10, 3);
        }


    private void TabControl1_DrawItem(object sender, System.Windows.Forms.DrawItemEventArgs e)
        {
            try
            {
                Image img = new Bitmap(CloseImage);
                Rectangle r = e.Bounds;
                r = this.tabControl1.GetTabRect(e.Index);
                r.Offset(2, 2);
                Brush TitleBrush = new SolidBrush(Color.Black);
                Font f = this.Font;
                string title = this.tabControl1.TabPages[e.Index].Text;

                e.Graphics.DrawString(title, f, TitleBrush, new PointF(r.X, r.Y));

                if (tabControl1.SelectedIndex >= 1)
                {
                    e.Graphics.DrawImage(img, new Point(r.X + (this.tabControl1.GetTabRect(e.Index).Width - _imageLocation.X), _imageLocation.Y));
                }
            }
            catch (Exception) { }
        }


private void TabControl1_Mouse_Click(object sender, System.Windows.Forms.DrawItemEventArgs e)
{

TabControl tc = (TabControl)sender;
Point p = e.Location;
int _tabWidth = 0;
_tabWidth = this.tabControl1.GetTabRect(tc.SelectedIndex).Width - (_imgHitArea.X);
Rectangle r = this.tabControl1.GetTabRect(tc.SelectedIndex);
r.Offset(_tabWidth, _imgHitArea.Y);
r.Width = 16;
r.Height = 16;
if (tabControl1.SelectedIndex >= 1)
{
    if (r.Contains(p))
    {
        TabPage TabP = (TabPage)tc.TabPages[tc.SelectedIndex];
        tc.TabPages.Remove(TabP);
    }

}
}

Look at this code snippet

user4340666
  • 1,453
  • 15
  • 36
  • 1
    don't forget to dispose the img object after draw, and e.index > 0 draw, and can invert colors for selected/unselected tabs. – antonio Oct 18 '18 at 10:55
1

I ran into this same issue, however, I also wanted to have an image in front of the caption on the tab. Here is the code that will give you an image in front, a caption and a close button. Also, this code includes the ability to use the middle mouse click on the tab to close it.

First under the TabControl's properties change DrawMode from Normal to OwnerDrawFixed. Next, you will need to tweak the X dimension for the TabControl's padding also found under properties.

In this example, my TabControl is called "TabManager" so rename that to the name of your control.

Go under the envents (the little lightning bolt near the top of the properties box) and double click in the white box next to "DrawItem", this will create a trigger. In that method use this code with your adjustments.

    private void TabManager_DrawItem(object sender, DrawItemEventArgs e)
    {
        TabPage thisTab = TabManager.TabPages[e.Index];
        string tabTitle = thisTab.Text;
        Image icon = imageList1.Images[thisTab.ImageIndex];
        //Draw Close button
        Point closeLoc = new Point(15, 5);
        e.Graphics.DrawRectangle(Pens.Black, e.Bounds.Right - closeLoc.X, e.Bounds.Top + closeLoc.Y, 10, 12);
        e.Graphics.FillRectangle(Brushes.LightBlue, e.Bounds.Right - closeLoc.X, e.Bounds.Top + closeLoc.Y, 10, 12);
        e.Graphics.DrawString("x", e.Font, Brushes.Black, e.Bounds.Right - (closeLoc.X), e.Bounds.Top + closeLoc.Y-2);
        // Draw String of Caption
        e.Graphics.DrawString(tabTitle, e.Font, Brushes.Black, e.Bounds.Left + 28, e.Bounds.Top + 4);
        // Add Icon to Front
        e.Graphics.DrawImage(icon, e.Bounds.Left + 6, e.Bounds.Top + 3);
        e.DrawFocusRectangle();
    }

Next go back to the events and double click on the white box next to MouseDown. Then use this code:

    private void TabManager_MouseDown(object sender, MouseEventArgs e)
    {
        Point closeLoc = new Point(15, 5);
        Rectangle r = TabManager.GetTabRect(TabManager.SelectedIndex);
        Rectangle closeButton = new Rectangle(r.Right - closeLoc.X, r.Top + closeLoc.Y, 10, 12);
        if (closeButton.Contains(e.Location))
        {
            TabManager.TabPages.Remove(TabManager.SelectedTab);
            return; // Don't keep running logic in method
        }
        for (int i = 0; i < TabManager.TabCount; i++)
        {
            r = TabManager.GetTabRect(i);
            if (r.Contains(e.Location) && e.Button == MouseButtons.Middle)
            {
                TabManager.TabPages.RemoveAt(i);
            }
        }
    }
Questionable
  • 768
  • 5
  • 26