TabControl con el botón Cerrar y Agregar

Estoy tratando de hacer que un control de tabs tenga una “x” (botón de cerrar) y “+” (botón de nueva pestaña). Encontré una solución para agregar un x button , la pestaña se ve así ahora:

enter image description here

Pero quiero agregar un + dónde está ese círculo negro en este momento. No tengo idea de cómo, intenté dibujar en el evento Paint de la última pestaña, así:

 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); } 

Pero no mostró nada dibujar. Supongo que tiene que ver con las posiciones que pasé a llamar a DrawString() , pero no sé cuáles usar. Usé +10 para retirarlo de la última pestaña. ¿Cómo arreglar eso? No he hecho ningún dibujo personalizado, lo estoy aprendiendo.

Como una opción, puede agregar una pestaña adicional que muestra un ícono de agregar Añadir y verifique cuando el usuario haga clic en esa pestaña, luego inserte una nueva TabPage antes.

También puedes evitar seleccionar esa pestaña adicional simplemente usando Selecting evento de TabControl . De esta forma, la última pestaña solo funciona como un botón para agregar, como IE y Chrome.

Pestaña con botón para cerrar y agregar

Detalles de implementacion

Utilizaremos una pestaña de dibujo del propietario para mostrar íconos cercanos en cada pestaña y un ícono de agregar en la última pestaña. Usamos DrawItem para dibujar cerrar y agregar icons, MouseDown para manejar haga clic en cerrar y agregar botones, Selecting para evitar la selección de la última pestaña y HandleCreated para ajustar el ancho de la pestaña. Puede ver todos los ajustes y códigos de implementación a continuación.

Inicialización

Establezca el relleno y DrawMode y asigne controladores de evento para DrawItem , MouseDown , HandleCreated y 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; 

Manejar hacer clic en el botón de cerrar y agregar el botón

Puede manejar el evento MouseDown o MouseClick y verificar si el último rectángulo de tabs contiene el punto del mouse, luego inserte una pestaña antes de la última pestaña. Otros verifican si uno de los botones cercanos contiene la ubicación cliqueada, luego cierre la pestaña en la que se hizo clic en el botón de cerrar:

 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; } } } } 

Evita seleccionar en la última pestaña

Para evitar la selección de la última pestaña, puede manejar la Selecting evento de control y comprobar si la pestaña de selección es la última pestaña, cancelar el evento:

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

Dibujar botón Cerrar y Agregar Botón

Para dibujar el botón de cerrar y agregar botón, puede manejar el evento DrawItem . Usé estos íconos para agregar Añadir y cerca Cerca botones.

 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); } } 

Ajuste el ancho de la pestaña

Para ajustar el ancho de las tabs y dejar que la última pestañeta tenga un ancho más pequeño, puede usar el evento HandleCreated y enviar un TCM_SETMINTABWIDTH al control y especificar el tamaño mínimo permitido para el ancho de la pestaña:

 [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); } 

Descargar

Puede descargar el código o clonar el repository aquí:

  • r-aghaei / TabControlWithCloseButtonAndAddButton

Normalmente, la forma directa de “bajo nivel” de hacer algo como esto sería manejar el evento Paint y dibujar en el propio TabControl , y luego también manejar los eventos de entrada del mouse para detectar los clics donde ha dibujado.

Sin embargo, a) eso es un problema, yb) el TabControl suprime el evento Paint , por lo que no es posible manejarlo sin ir incluso a un nivel inferior y tratar con el mensaje WM_PAINT en un método WndProc() .

Para sus propósitos, recomendaría simplemente agregar un nuevo control, por ejemplo, un Button , al Form , colocándolo justo sobre el lugar en el TabControl donde desea que el usuario pueda hacer clic. Luego, en el Button.Click eventos Button.Click , puede agregar una nueva página como desee. Si desea encapsular la combinación de Button y TabControl , puede usar UserControl .

Por ejemplo:

TabControlWithAdd.Designer.cs:

 partial class TabControlWithAdd { ///  /// Required designer variable. ///  private System.ComponentModel.IContainer components = null; ///  /// Clean up any resources being used. ///  /// true if managed resources should be disposed; otherwise, false. protected override void Dispose(bool disposing) { if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); } #region Component Designer generated code ///  /// Required method for Designer support - do not modify /// the contents of this method with the code editor. ///  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)); } } 

Lo anterior usa el Button , pero por supuesto puede usar cualquier otro control seleccionable que desee, incluyendo Label (por ejemplo, si no desea la apariencia del borde del botón), para producir el efecto visual que desea.

Otra forma de hacerlo es crear un nuevo TabControl que extienda la clase TabControl . Tuve el mismo problema una vez y así fue como lo hice, no pude encontrar el código terminado, pero esto funcionará al agregar una X a sus tabs, lo mismo se puede aplicar para el signo + :

 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, rX +10 + MoveRight - 2, rY, rX +10 + MoveRight + r.Width, rY + r.Height+2); e.Graphics.DrawLine(p, rX +10 + MoveRight + r.Width, rY, rX + 10 + MoveRight-2, rY + r.Height+2); e.Graphics.DrawString(boldLetter, f1, b1, new PointF(rX, rY)); 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(rX + 5, rY)); } } 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; rX = rX + 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]); } } 

Puede intentar modificarlo un poco hasta que se adapte a sus necesidades.