[英]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
并为DrawItem
, MouseDown
, Selecting
和HandleCreated
事件分配事件处理程序。
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. 您可以处理
MouseDown
或MouseClick
事件,并检查最后一个选项卡矩形是否包含鼠标单击的点,然后在最后一个选项卡之前插入选项卡。 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
. 如果要封装
Button
和TabControl
的组合,可以使用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.