Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / desktop / WinForms

ClosableTab Control

2.44/5 (6 votes)
20 May 2007CPOL3 min read 1   840  
A ClosableTabs control which extends the functionality of the WinForms TabControl to display a close image.

Screenshot - ClosableTabs.gif

Introduction

I was in the need of a tab control that allows users to close tab pages by clicking on a close sign. While searching the net, I came across this great article by Yoramo. The only problem with it was that the close sign was added to the left-hand side of the text while my users where expecting it on the right-hand side. So I simply re-wrote the code provided by Yoramo, and after some hacking, I got it up and running the way I wanted to, and so I thought I'd share it with you all here at The Code Project.

Using the Control

Using the control is actually quite easy. All you need to do is:

  1. Create a new Windows Application Project and name it whatever you want (e.g.: ClosableTabs).
  2. Download the provided zip-file and extract it into your project folder.
  3. Right-click on your project in the Solution Explorer, choose Add => Existing Item, then browse your way to ClosableTabs.cs.
  4. Right click on the Form1.cs file in Solution Explorer and choose "View Code".
  5. Add the following using statement in Form1.cs: using PL.TabControl;. Now hit the F6 key.
  6. Your code should now look something like this:

    C#
    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Drawing;
    using System.Text;
    using System.Windows.Forms;
    
    using PL.TabControl;
    
    namespace ClosableTabs
    {
        public partial class Form1 : Form
        {
            public Form1()
            {
                InitializeComponent();
            }
        }
    }
  7. Now open Form Designer (double click the Form1.cs file in Solution Explorer).
  8. In the Toolbox list, a component called ClosableTabs should now be added. Click and drag it onto the designer surface as you would with any other control.
  9. Make sure that the ClosableTabs control is selected (Note: the whole control must be selected; you can ensure this by clicking once on one of the tabs).
  10. In the "Properties" window, search the list for the "SetImage" property, then click the [...] button.
  11. In the "Select Resource" dialog, click the "Local Resource" radio button, then click "Import". Browse your way to the "close.gif" file and click Open.
  12. Back in the "Select Resource" dialog, click "OK" and then hit the F6 key, and the close image should appear on the tabs.

And that's all there really is to it; whenever you add a new tab, it will automatically get a close image on it.

Points of Interest

Adding your own closing sign image won't cause any problems as long as it's the same size as the provided image (10x10 pixels), but if it's bigger/smaller, there are a few additional steps to follow.

  1. First of all, you might want to change the location of the image; this is done by changing the ImageLocation property of the ClosableTabs control.
  2. Note: The ImageLocation property counts backwards, so by increasing it, the image will move to the left, and decreasing it will move the image to the right.

  3. Secondly, you will probably need to change the ImageHitArea property of the ClosableTabs control.
  4. Note: Setting the ImageHitArea property is done by guessing your way to the right number. You can use this as a rule of thumb: ImageLocation.X - image width - x = 2; where x is the number to increase the ImageHitArea.X value with. For example:

    • 20 - 10 - 8 = 2; ImageHitArea.X = image width + 8;
    • 15 - 10 - 3 = 2; ImageHitArea.X = image width + 3;
    • 17 - 10 - 5 = 2; ImageHitArea.X = image width + 5;

Note: The ImageHitArea.Y value should always be 2 unless you change the ImageLocation.Y property, and should then be increased/decreased with the same value as the ImageLocation.Y property was. For example, if you increase ImageLocation.Y from 5 to 7 (an increment by 2), then ImageHitArea.Y should be increased from 2 to 4.

Please post a comment if you've got an idea of how to improve this or any other part of the control, and I'll be happy to implement it.

The ClosableTabs Code

C#
using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;
using System.Drawing;

namespace PL.TabControl
{
    public delegate bool OnBeforeCloseTab(int indx);

    class ClosableTab : System.Windows.Forms.TabControl
    {
        public event OnBeforeCloseTab BeforeCloseATabPage;

        private Image _img;
        private Point _imageLocation = new Point(15, 5);
        private Point _imgHitArea = new Point(13, 2);
        
        private bool _imgExist = false;
        private int _tabWidth = 0;

        public ClosableTab()
            : base()
        {
            BeforeCloseATabPage = null;
            this.DrawMode = TabDrawMode.OwnerDrawFixed;
            this.Padding = new Point(12, 3);
        }

        public Image SetImage
        {
            set 
            { 
                _img = value;
                if (_img != null)
                {
                    _imgExist = true;
                }
            }
            get { return _img; }
        }

        public Point ImageLocation
        {
            get { return _imageLocation; }
            set { _imageLocation = value; }
        }

        public Point ImageHitArea
        {
            get { return _imgHitArea; }
            set { _imgHitArea = value; }
        }

        protected override void OnDrawItem(DrawItemEventArgs e)
        {
            Rectangle r = e.Bounds;
            r = GetTabRect(e.Index);
            r.Offset(2, 2);
            
            Brush TitleBrush = new SolidBrush(Color.Black);
            Font f = this.Font;
            
            string title = this.TabPages[e.Index].Text;
                        
            e.Graphics.DrawString(title, f, TitleBrush, new PointF(r.X, r.Y));

            try
            {
                e.Graphics.DrawImage(_img, new Point(r.X + 
                   (GetTabRect(e.Index).Width - _imageLocation.X), _imageLocation.Y));
            }
            catch (Exception) { }
        }

        protected override void OnMouseClick(MouseEventArgs e)
        {
            if (_imgExist == true)
            {
                Point p = e.Location;
                for (int i = 0; i < TabCount; i++)
                {
                    _tabWidth = GetTabRect(i).Width - (_imgHitArea.X);

                    Rectangle r = GetTabRect(i);
                    r.Offset(_tabWidth, _imgHitArea.Y);
                    r.Width = _img.Width;
                    r.Height = _img.Height;
                    if (r.Contains(p))
                    {
                        CloseTab(i);
                    }
                }
            }
        }

        private void CloseTab(int i)
        {
            if (BeforeCloseATabPage != null)
            {
                bool CanClose = BeforeCloseATabPage(i);
                if (!CanClose)
                    return;
            }
            TabPages.Remove(TabPages[i]);
        }
    }
}

License

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