BLOG.CSHARPHELPER.COM: Make a TabControl use owner-drawn tabs and let the user close tabs at run time in C#
Make a TabControl use owner-drawn tabs and let the user close tabs at run time in C#
To make an owner drawn TabControl, the form's Load event handler sets the control's DrawMode property to OwnerDrawFixed. The SizeMode must be Fixed if the program needs to change the tab size. This example does change the size to 10 pixels wide and 6 pixels taller than the initial default.
private void Form1_Load(object sender, EventArgs e) { // We will draw the tabs. tabMenu.DrawMode = TabDrawMode.OwnerDrawFixed;
// SizeMode must be Fixed to change tab size. tabMenu.SizeMode = TabSizeMode.Fixed;
// Set the size for the tabs. Size tab_size = tabMenu.ItemSize; tab_size.Width = 100; tab_size.Height += 6; tabMenu.ItemSize = tab_size; }
When the control needs to draw a tab, it raises its DrawItem event handler.
// The size of the X in each tab's upper right corner. private int Xwid = 8; private const int tab_margin = 3;
// Allow room for margins. RectangleF layout_rect = new RectangleF( tab_rect.Left + tab_margin, tab_rect.Y + tab_margin, tab_rect.Width - 2 * tab_margin, tab_rect.Height - 2 * tab_margin); using (StringFormat string_format = new StringFormat()) { // Draw the tab # in the upper left corner. using (Font small_font = new Font(this.Font.FontFamily, 6, FontStyle.Bold)) { string_format.Alignment = StringAlignment.Near; string_format.LineAlignment = StringAlignment.Near; e.Graphics.DrawString( e.Index.ToString(), small_font, txt_brush, layout_rect, string_format); }
// Draw the tab's text centered. using (Font big_font = new Font(this.Font, FontStyle.Bold)) { string_format.Alignment = StringAlignment.Center; string_format.LineAlignment = StringAlignment.Center; e.Graphics.DrawString( tabMenu.TabPages[e.Index].Text, big_font, txt_brush, layout_rect, string_format); }
// Draw an X in the upper right corner. Rectangle rect = tabMenu.GetTabRect(e.Index); e.Graphics.FillRectangle(box_brush, layout_rect.Right - Xwid, layout_rect.Top, Xwid, Xwid); e.Graphics.DrawRectangle(box_pen, layout_rect.Right - Xwid, layout_rect.Top, Xwid, Xwid); e.Graphics.DrawLine(box_pen, layout_rect.Right - Xwid, layout_rect.Top, layout_rect.Right, layout_rect.Top + Xwid); e.Graphics.DrawLine(box_pen, layout_rect.Right - Xwid, layout_rect.Top + Xwid, layout_rect.Right, layout_rect.Top); }
// Note: Don't Dispose the stock pens and brushes. }
The DrawItem event handler routine gets the tab's area by using the control's GetTabRect method. This returns a rectangle that is slightly different from the one in the e.Bounds parameter and is the rectangle that we can use later in the MouseDown event handler.
DrawItem then checks the e.State parameter to see whether the tab is selected and picks colors accordingly. It fills the tab's background and, if the tab is selected, it draws a focus rectangle on it.
Next the code makes a rectangle representing the tab's area minus some margins. It uses this rectangle to position the tab number in the upper left corner and the tab's text centered. The program then draws an X in the upper right corner.
When the user clicks on a tab, the program determines whether the mouse is over any tab's X. If the mouse is over an X, the program removes the corresponding tab.
// If the mouse is over an X, close the tab. private void tabMenu_MouseDown(object sender, MouseEventArgs e) { // See if this is over a tab. for (int i = 0; i < tabMenu.TabPages.Count; i++) { // Get the TabRect plus room for margins. Rectangle tab_rect = tabMenu.GetTabRect(i); RectangleF rect = new RectangleF( tab_rect.Left + tab_margin, tab_rect.Y + tab_margin, tab_rect.Width - 2 * tab_margin, tab_rect.Height - 2 * tab_margin); if (e.X >= rect.Right - Xwid && e.X <= rect.Right && e.Y >= rect.Top && e.Y <= rect.Top + Xwid) { Console.WriteLine("Tab " + i); tabMenu.TabPages.RemoveAt(i); return; } } }
Comments