TabControl with Close and Add Button

前端 未结 3 1009
难免孤独
难免孤独 2020-11-27 21:01

I\'m tring to make a tab control have a \"x\" (close button) and \"+\" (new tab button). I found a solution to add a x button, the tab looks like this now:

相关标签:
3条回答
  • 2020-11-27 21:32

    Another way of going about it is creating a new TabControl which extends the TabControl class. I had the same issue once and that was how I did it, I couldn't find the finished code but this will work in adding an X to your tabs, the same can be applied for the + sign:

    public delegate bool PreRemoveTab(int indx);
    public class TabControlEx : TabControl
    {
        public TabControlEx()
            : base()
        {
            PreRemoveTabPage = null;
            this.DrawMode = TabDrawMode.OwnerDrawFixed;
        }
    
        public PreRemoveTab PreRemoveTabPage;
    
        protected const int size = 5;
    
        protected int moveRight = 0;
    
        protected int MoveRight
        {
            get { return moveRight; }
            set { moveRight = value; }
        }
    
        protected override void OnDrawItem(DrawItemEventArgs e)
        {
            Brush b = new SolidBrush(Color.Salmon);
            Brush b1 = new SolidBrush(Color.Black);
            Font f = this.Font;
            Font f1 = new Font("Arial", 9,FontStyle.Bold);
            if (e.Index != 0)
            {
                Rectangle r = e.Bounds;
                r = GetTabRect(e.Index);
                r.Offset(2, 2);
                r.Width = size;
                r.Height = size;               
                Pen p = new Pen(b,2);
                string title = this.TabPages[e.Index].Text;               
                string boldLetter = title.Substring(0, 1);
                title = title.Remove(0, 1);
                MoveRight = ((Int32)e.Graphics.MeasureString(title, f, 200).Width) + 1;   // -1
                e.Graphics.DrawLine(p, r.X +10 + MoveRight - 2, r.Y, r.X +10 + MoveRight + r.Width, r.Y + r.Height+2);
                e.Graphics.DrawLine(p, r.X +10 + MoveRight + r.Width, r.Y, r.X + 10 + MoveRight-2, r.Y + r.Height+2);
                e.Graphics.DrawString(boldLetter, f1, b1, new PointF(r.X, r.Y));
                e.Graphics.DrawString(title, f, b1, new PointF(r.X+8, r.Y+1));    
            }
            else
            {
                Rectangle r = GetTabRect(e.Index);
                e.Graphics.DrawString(this.TabPages[e.Index].Text, f, b1, new PointF(r.X + 5, r.Y));
            }
        }
    
        protected override void OnMouseClick(MouseEventArgs e)
        {
            Point p = e.Location;
            for (int i = 0; i < TabCount; i++)
            {
                Rectangle r = GetTabRect(i);
                r.Offset(2, 2);
                r.Width = size+2;
                r.Height = size+2;
                r.X = r.X + MoveRight + 8;
                if (r.Contains(p))
                {
                    if (i != 0)
                    {
                        CloseTab(i);
                    }
                }
            }
        }
    
        private void CloseTab(int i)
        {
            if (PreRemoveTabPage != null)
            {
                bool closeIt = PreRemoveTabPage(i);
                if (!closeIt)
                    return;
            }
            TabPages.Remove(TabPages[i]);
        }
    }
    

    You can try modifying it a bit until it suits your needs.

    0 讨论(0)
  • 2020-11-27 21:33

    As an option you can add an extra tab which shows an add icon Add and check when the user clicks on that tab, then insert a new TabPage before it.

    Also you can prevent selecting that extra tab simply using Selecting event of TabControl. This way the last tab acts only like an add button for you, like IE and Chrome.

    Tab with close and add button

    Implementation Details

    We will use an owner draw tab to show close icons on each tab an add icon on the last tab. We use DrawItem to draw close and add icons, MouseDown to handle click on close and add buttons, Selecting to prevent selecting of the last tab and HandleCreated to adjust tab width. You can see all implementation settings and codes below.

    Initialization

    Set padding and DrawMode and assign event handlers for DrawItem, MouseDown, Selecting and HandleCreated event.

    this.tabControl1.Padding = new Point(12, 4);
    this.tabControl1.DrawMode = TabDrawMode.OwnerDrawFixed;
    
    this.tabControl1.DrawItem += tabControl1_DrawItem;
    this.tabControl1.MouseDown += tabControl1_MouseDown;
    this.tabControl1.Selecting += tabControl1_Selecting;
    this.tabControl1.HandleCreated += tabControl1_HandleCreated;
    

    Handle click on close button and add button

    You can handle MouseDown or MouseClick event and check if the last tab rectangle contains the mouse clicked point, then insert a tab before the last tab. Otherwose check if one of close buttons contains clicked location, then close the tab which its close button was clicked:

    private void tabControl1_MouseDown(object sender, MouseEventArgs e)
    {
        var lastIndex = this.tabControl1.TabCount - 1;
        if (this.tabControl1.GetTabRect(lastIndex).Contains(e.Location))
        {
            this.tabControl1.TabPages.Insert(lastIndex, "New Tab");
            this.tabControl1.SelectedIndex = lastIndex;
        }
        else
        {
            for (var i = 0; i < this.tabControl1.TabPages.Count; i++)
            {
                var tabRect = this.tabControl1.GetTabRect(i);
                tabRect.Inflate(-2, -2);
                var closeImage = Properties.Resources.DeleteButton_Image;
                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.tabControl1.TabPages.RemoveAt(i);
                    break;
                }
            }
        }
    }
    

    Prevent selectin last tab

    To prevent selection the last tab, you can handle Selecting event of control and check if the selecting tab is the last tab, cancel the event:

    private void tabControl1_Selecting(object sender, TabControlCancelEventArgs e)
    {
        if (e.TabPageIndex == this.tabControl1.TabCount - 1)
            e.Cancel = true;
    }
    

    Draw Close Button and Add Button

    To draw close button and add button, you can handle DrawItem event. I used these icons for add Add and close Close buttons.

    private void tabControl1_DrawItem(object sender, DrawItemEventArgs e)
    {
        var tabPage = this.tabControl1.TabPages[e.Index];
        var tabRect = this.tabControl1.GetTabRect(e.Index);
        tabRect.Inflate(-2, -2);
        if (e.Index == this.tabControl1.TabCount - 1)
        {
            var addImage = Properties.Resources.AddButton_Image;
            e.Graphics.DrawImage(addImage,
                tabRect.Left + (tabRect.Width - addImage.Width) / 2,
                tabRect.Top + (tabRect.Height - addImage.Height) / 2);
        }
        else
        {
            var closeImage = Properties.Resources.DeleteButton_Image;
            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);
        }
    }
    

    Adjust Tab width

    To adjust tab width and let the last tab have smaller width, you can hanlde HandleCreated event and send a TCM_SETMINTABWIDTH to the control and specify the minimum size allowed for the tab width:

    [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.tabControl1.Handle, TCM_SETMINTABWIDTH, IntPtr.Zero, (IntPtr)16);
    }
    

    Download

    You can download the code or clone the repository here:

    • r-aghaei/TabControlWithCloseButtonAndAddButton
    0 讨论(0)
  • 2020-11-27 21:46

    Normally, the direct, "low-level" way to do something like this would be to handle the Paint event and draw into the TabControl itself, and then also handle mouse input events to detect clicks where you have drawn.

    However, a) that's a pain, and b) the TabControl suppresses the Paint event, so it's not possible to handle without going even lower-level and dealing with the WM_PAINT message in a WndProc() method override.

    For your purposes, I would recommend simply adding a new control, e.g. a Button, to the Form, placing it just over the place on the TabControl where you want the user to be able to click. Then in the Button.Click event handler, you can add a new page as desired. If you want to encapsulate the combination of the Button and the TabControl, you can use a UserControl.

    For example:

    TabControlWithAdd.Designer.cs:

    partial class TabControlWithAdd
    {
        /// <summary> 
        /// Required designer variable.
        /// </summary>
        private System.ComponentModel.IContainer components = null;
    
        /// <summary> 
        /// Clean up any resources being used.
        /// </summary>
        /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
        protected override void Dispose(bool disposing)
        {
            if (disposing && (components != null))
            {
                components.Dispose();
            }
            base.Dispose(disposing);
        }
    
        #region Component Designer generated code
    
        /// <summary> 
        /// Required method for Designer support - do not modify 
        /// the contents of this method with the code editor.
        /// </summary>
        private void InitializeComponent()
        {
            this.button1 = new System.Windows.Forms.Button();
            this.tabControl1 = new System.Windows.Forms.TabControl();
            this.tabPage1 = new System.Windows.Forms.TabPage();
            this.tabPage2 = new System.Windows.Forms.TabPage();
            this.tabControl1.SuspendLayout();
            this.SuspendLayout();
            // 
            // button1
            // 
            this.button1.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
            this.button1.Location = new System.Drawing.Point(247, 3);
            this.button1.Name = "button1";
            this.button1.Size = new System.Drawing.Size(23, 23);
            this.button1.TabIndex = 0;
            this.button1.Text = "+";
            this.button1.UseVisualStyleBackColor = true;
            this.button1.Click += new System.EventHandler(this.button1_Click);
            // 
            // tabControl1
            // 
            this.tabControl1.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
            | System.Windows.Forms.AnchorStyles.Left) 
            | System.Windows.Forms.AnchorStyles.Right)));
            this.tabControl1.Controls.Add(this.tabPage1);
            this.tabControl1.Controls.Add(this.tabPage2);
            this.tabControl1.Location = new System.Drawing.Point(3, 3);
            this.tabControl1.Name = "tabControl1";
            this.tabControl1.SelectedIndex = 0;
            this.tabControl1.Size = new System.Drawing.Size(267, 181);
            this.tabControl1.TabIndex = 1;
            // 
            // tabPage1
            // 
            this.tabPage1.Location = new System.Drawing.Point(4, 25);
            this.tabPage1.Name = "tabPage1";
            this.tabPage1.Padding = new System.Windows.Forms.Padding(3);
            this.tabPage1.Size = new System.Drawing.Size(259, 152);
            this.tabPage1.TabIndex = 0;
            this.tabPage1.Text = "tabPage1";
            this.tabPage1.UseVisualStyleBackColor = true;
            // 
            // tabPage2
            // 
            this.tabPage2.Location = new System.Drawing.Point(4, 25);
            this.tabPage2.Name = "tabPage2";
            this.tabPage2.Padding = new System.Windows.Forms.Padding(3);
            this.tabPage2.Size = new System.Drawing.Size(192, 71);
            this.tabPage2.TabIndex = 1;
            this.tabPage2.Text = "tabPage2";
            this.tabPage2.UseVisualStyleBackColor = true;
            // 
            // TabControlWithAdd
            // 
            this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 16F);
            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
            this.Controls.Add(this.button1);
            this.Controls.Add(this.tabControl1);
            this.Name = "TabControlWithAdd";
            this.Size = new System.Drawing.Size(273, 187);
            this.tabControl1.ResumeLayout(false);
            this.ResumeLayout(false);
    
        }
    
        #endregion
    
        private System.Windows.Forms.Button button1;
        private System.Windows.Forms.TabControl tabControl1;
        private System.Windows.Forms.TabPage tabPage1;
        private System.Windows.Forms.TabPage tabPage2;
    }
    

    TabControlWithAdd.cs:

    public partial class TabControlWithAdd : UserControl
    {
        public TabControlWithAdd()
        {
            InitializeComponent();
        }
    
        private void button1_Click(object sender, EventArgs e)
        {
            tabControl1.TabPages.Add("Tab " + (tabControl1.TabPages.Count + 1));
        }
    }
    

    The above uses Button, but of course you can use any other clickable control you like, including Label (e.g. if you don't want the button border appearance), to produce the visual effect you want.

    0 讨论(0)
提交回复
热议问题