TabControl с кнопкой Close и Add
Я пытаюсь сделать вкладку управления "X" (кнопка закрытия) и "+" (кнопка новой вкладки). Я нашел решение добавить x button
, теперь вкладка выглядит так:
![enter image description here]()
Но я хочу добавить +
, где этот черный круг прямо сейчас. Понятия не имею, как, я пытался нарисовать событие 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);
}
Но это ничего не показывало. Я предполагаю, что это связано с позициями, которые я передал для вызова DrawString()
, но я не знаю, какие из них использовать. Я использовал +10, чтобы вывести его из последней вкладки. Как это исправить? Я не делал никаких пользовательских рисунков, я учусь этому.
Ответы
Ответ 1
В качестве опции вы можете добавить дополнительную вкладку со значком добавления
и проверить, когда пользователь нажимает на эту вкладку, а затем вставить новую TabPage
перед ней.
Также вы можете предотвратить выбор этой дополнительной вкладки, просто используя событие Selecting
из TabControl
. Таким образом, последняя вкладка действует только как кнопка добавления, например, IE и Chrome.
![Tab with close and add button]()
Детали реализации
Мы будем использовать вкладку рисования владельца, чтобы показывать значки закрытия на каждой вкладке и значок добавления на последней вкладке. Мы используем DrawItem
для рисования и добавления значков, MouseDown
для обработки нажатия на кнопки закрытия и добавления кнопок, Selecting
для предотвращения выбора последней вкладки и HandleCreated
для регулировки ширины вкладки. Вы можете увидеть все настройки реализации и коды ниже.
Инициализация
Установите отступы и 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;
Ручка, нажмите на кнопку закрытия и добавьте кнопку
Вы можете обработать событие MouseDown
или MouseClick
и проверить, содержит ли прямоangularьник последней вкладки точку щелчка мыши, затем вставить вкладку перед последней вкладкой. В противном случае проверьте, содержит ли одна из кнопок закрытия нажатое местоположение, затем закройте вкладку, на которой была нажата кнопка закрытия:
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;
}
}
}
}
Запретить выбор на последней вкладке
Чтобы предотвратить выбор последней вкладки, вы можете обработать событие управления Selecting
и проверить, является ли выбранная вкладка последней вкладкой, отменить событие:
private void tabControl1_Selecting(object sender, TabControlCancelEventArgs e)
{
if (e.TabPageIndex == this.tabControl1.TabCount - 1)
e.Cancel = true;
}
Кнопка "Закрыть" и "Добавить"
Чтобы нарисовать кнопку закрытия и добавить кнопку, вы можете обработать событие DrawItem
. Я использовал эти значки для добавления кнопок
и закрытия кнопок
.
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);
}
}
Отрегулируйте ширину вкладки
Чтобы настроить ширину вкладки и позволить последней вкладке иметь меньшую ширину, вы можете передать событие 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);
}
Скачать
Вы можете скачать код или клонировать репозиторий здесь:
Ответ 2
Как правило, прямой, "низкоуровневый" способ сделать что-то вроде этого, должен обрабатывать событие Paint
и рисовать в самом TabControl
, а затем также обрабатывать события ввода мыши для обнаружения кликов, где у вас есть обращается.
Тем не менее, a) что боль и b) TabControl
подавляет событие Paint
, поэтому его невозможно обрабатывать, не переходя еще на нижний уровень и не имея дело с сообщением WM_PAINT
в WndProc()
метод переопределения.
В ваших целях я бы рекомендовал просто добавить новый элемент управления, например. a Button
, в Form
, разместив его как раз над местом на TabControl
, где вы хотите, чтобы пользователь мог щелкнуть. Затем в обработчике событий Button.Click
вы можете добавить новую страницу по своему желанию. Если вы хотите инкапсулировать комбинацию Button
и TabControl
, вы можете использовать UserControl
.
Например:
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));
}
}
В приведенном выше примере используется Button
, но, конечно, вы можете использовать любой другой доступный для вас контроль, включая Label
(например, если вы не хотите внешний вид кнопки), чтобы создать необходимый визуальный эффект.
Ответ 3
Еще один способ сделать это - создать новый TabControl
, который расширяет класс TabControl
. Однажды у меня была одна и та же проблема, и я так и не смог найти готовый код, но это будет работать с добавлением 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]);
}
}
Вы можете попробовать немного изменить его, пока он не удовлетворит ваши потребности.