简体   繁体   English

带关闭和添加按钮的TabControl

[英]TabControl with Close and Add Button

I'm tring to make a tab control have a "x" (close button) and "+" (new tab button). 我想让一个标签控件有一个“x”(关闭按钮)和“+”(新标签按钮)。 I found a solution to add a x button , the tab looks like this now: 我找到了一个添加x button的解决方案,该选项卡现在看起来像这样:

在此输入图像描述

But I want to add a + where's that black circle right now. 但是我想现在添加一个+那个黑色圆圈的地方。 I have no idea how, I tried draw on Paint event of the last tab, like this: 我不知道如何,我尝试绘制最后一个标签的Paint事件,如下所示:

var p = tabs.TabPages[tabs.TabCount - 1];
p.Paint += new PaintEventHandler(tab_OnDrawPage);

private void tab_OnDrawPage(object sender, PaintEventArgs e)
{
    // e.ClipRectangle.
    e.Graphics.DrawString("+", 
                          new Font("verdana", 
                                   10, 
                                   FontStyle.Bold), 
                          Brushes.Black, 
                          e.ClipRectangle.X + 10, 
                          e.ClipRectangle.Y + 10);
}

But it didn't show anything draw. 但它并没有显示任何吸引力。 I guess it has to do with the positions I passed to DrawString() call, but I don't know the proper ones to use. 我想这与我传递给DrawString()调用的位置有关,但我不知道要使用的正确位置。 I used +10 to draw it away from last tab. 我使用+10将它从最后一个标签中拉出来。 How to fix that?. 如何解决? I haven't done any custom drawing myself, I'm learning it. 我自己没有做任何自定义绘图,我正在学习它。

As an option you can add an extra tab which shows an add icon 作为选项,您可以添加一个显示添加图标的额外选项卡 加 and check when the user clicks on that tab, then insert a new TabPage before it. 并检查用户何时单击该选项卡,然后在其前面插入新的TabPage

Also you can prevent selecting that extra tab simply using Selecting event of TabControl . 此外,您可以使用Selecting TabControl事件来阻止选择该额外选项卡。 This way the last tab acts only like an add button for you, like IE and Chrome. 这样,最后一个标签的行为就像一个添加按钮,如IE和Chrome。

选项卡与关闭和添加按钮

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. 我们使用DrawItem绘制关闭并添加图标,使用MouseDown处理单击关闭并添加按钮, Selecting以防止选择最后一个选项卡,使用HandleCreated调整选项卡宽度。 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. 设置padding和DrawMode并为DrawItemMouseDownSelectingHandleCreated事件分配事件处理程序。

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. 您可以处理MouseDownMouseClick事件,并检查最后一个选项卡矩形是否包含鼠标单击的点,然后在最后一个选项卡之前插入选项卡。 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 防止selectin最后一个标签

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: 要防止选择最后一个选项卡,您可以处理Selecting控件事件并检查选择选项卡是否是最后一个选项卡,取消该事件:

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. 要绘制关闭按钮并添加按钮,您可以处理DrawItem事件。 I used these icons for add 我用这些图标添加 加 and 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: 要调整选项卡宽度并让最后一个选项卡具有较小的宽度,您可以处理HandleCreated事件并将TCM_SETMINTABWIDTH发送到控件并指定选项卡宽度允许的最小大小:

[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: 您可以在此处下载代码或克隆存储库:

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. 通常,执行此类操作的直接“低级”方法是处理Paint事件并绘制到TabControl本身,然后还处理鼠标输入事件以检测您绘制的点击。

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. 然而,a)这是一个痛苦,并且b) TabControl抑制了Paint事件,因此在WndProc()方法覆盖中不进行更低级别处理WM_PAINT消息就不可能处理。

For your purposes, I would recommend simply adding a new control, eg a Button , to the Form , placing it just over the place on the TabControl where you want the user to be able to click. 为了您的目的,我建议您只需将新控件(例如Button )添加到Form ,将其放在TabControl上您希望用户可以单击的位置上。 Then in the Button.Click event handler, you can add a new page as desired. 然后在Button.Click事件处理程序中,您可以根据需要添加新页面。 If you want to encapsulate the combination of the Button and the TabControl , you can use a UserControl . 如果要封装ButtonTabControl的组合,可以使用UserControl

For example: 例如:

TabControlWithAdd.Designer.cs: 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: 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 (eg if you don't want the button border appearance), to produce the visual effect you want. 以上使用Button ,但当然你可以使用你喜欢的任何其他可点击控件,包括Label (例如,如果你不想要按钮边框外观),以产生你想要的视觉效果。

Another way of going about it is creating a new TabControl which extends the TabControl class. 另一种方法是创建一个新的TabControl ,它扩展了TabControl类。 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: 我曾经遇到过同样的问题,这就是我的做法,我找不到完成的代码,但这可以在你的标签上添加一个X ,同样可以用于+符号:

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. 您可以尝试稍微修改它,直到它符合您的需要。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM