Vertical tab control with horizontal text at design time - c#

A clear omission seems to be that after applying this approach:
Vertical Tab Control with horizontal text in Winforms
Which is also recommended by Microsoft:
How to: Display Side-Aligned Tabs with TabControl
There is no text on tabs at design time, so further development and support becomes a nightmare.
Is there a way to make tab text also display at design time?

Just create your own control so the custom drawing also works at design time. Add a new class to your project and paste the code shown below. Compile. Drop the new control from the top of the toolbox onto your form. I tweaked it a bit to make it no so garish.
using System;
using System.Drawing;
using System.Windows.Forms;
class VerticalTabControl : TabControl {
public VerticalTabControl() {
this.Alignment = TabAlignment.Right;
this.DrawMode = TabDrawMode.OwnerDrawFixed;
this.SizeMode = TabSizeMode.Fixed;
this.ItemSize = new Size(this.Font.Height * 3 / 2, 75);
}
public override Font Font {
get { return base.Font; }
set {
base.Font = value;
this.ItemSize = new Size(value.Height * 3 / 2, base.ItemSize.Height);
}
}
protected override void OnDrawItem(DrawItemEventArgs e) {
using (var _textBrush = new SolidBrush(this.ForeColor)) {
TabPage _tabPage = this.TabPages[e.Index];
Rectangle _tabBounds = this.GetTabRect(e.Index);
if (e.State != DrawItemState.Selected) e.DrawBackground();
else {
using (var brush = new System.Drawing.Drawing2D.LinearGradientBrush(e.Bounds, Color.White, Color.LightGray, 90f)) {
e.Graphics.FillRectangle(brush, e.Bounds);
}
}
StringFormat _stringFlags = new StringFormat();
_stringFlags.Alignment = StringAlignment.Center;
_stringFlags.LineAlignment = StringAlignment.Center;
e.Graphics.DrawString(_tabPage.Text, this.Font, _textBrush, _tabBounds, new StringFormat(_stringFlags));
}
}
}

You need to subclass the TabControl and override OnDrawItem. Here's an example:
Public Class UITabControl
Inherits TabControl
Protected Overrides Sub OnDrawItem(e As DrawItemEventArgs)
Using brush As New SolidBrush(Me.ForeColor)
Using format As New StringFormat() With {.LineAlignment = StringAlignment.Center}
Select Case Me.Alignment
Case TabAlignment.Left
format.Alignment = StringAlignment.Near
Case TabAlignment.Top
format.Alignment = StringAlignment.Far
End Select
format.FormatFlags = (format.FormatFlags Or StringFormatFlags.NoWrap)
Dim rect As Rectangle = e.Bounds
rect.X += 3
rect.Width -= 6
e.Graphics.DrawString(Me.TabPages(e.Index).Text, Me.Font, brush, rect, format)
End Using
End Using
MyBase.OnDrawItem(e)
End Sub
End Class

Since you linked to my question, I thought it expedient to inform you of updates to my question thread.
I have, in an answer to my question, uploaded my code for the control in the interest of the programming community.
This is a screenshot of the control at runtime.
It features full design time support, automatic resizing of tabs (up to 128px wide) and tab icons as well.
The code can be downloaded from here.

Related

Styling Windows Form Tab

I am creating windows Tabbed Application. Everything is good but the tabs are quiet faded and borders are very dull. I have tried changing the border style to 3D as well but no effect. Below is the screenshot
There are forums where people have suggested to use third party library to make Google Chrome type tabs. But I want the native way to get beautiful tabs.
You can take control of how the tabs are drawn by setting the DrawMode = TabDrawMode.OwnerDrawFixed. The example below assumes you have a TabControl named tabControl1 on the form, this will add a new tab with a blue box.
private Rectangle myTabRect;
private Rectangle myInsideRect;
private Rectangle myOutsideRect;
public Form1()
{
InitializeComponent();
TabPage tabPage1 = new TabPage();
// Sets the tabs to be drawn by the parent window Form1.
// OwnerDrawFixed allows access to DrawItem.
tabControl1.DrawMode = TabDrawMode.OwnerDrawFixed;
tabControl1.Controls.Add(tabPage1);
tabControl1.Location = new Point(25, 25);
tabControl1.Size = new Size(250, 250);
tabPage1.TabIndex = 0;
myTabRect = tabControl1.GetTabRect(0);
myInsideRect = new Rectangle(tabControl1.DisplayRectangle.X -1, tabControl1.DisplayRectangle.Y -1, tabControl1.DisplayRectangle.Width + 1, tabControl1.DisplayRectangle.Height + 1);
myOutsideRect = tabControl1.ClientRectangle;
myOutsideRect.Width--;
myOutsideRect.Height--;
ClientSize = new Size(300, 500);
Controls.Add(tabControl1);
tabControl1.DrawItem += new DrawItemEventHandler(OnDrawItem);
}
private void OnDrawItem(object sender, DrawItemEventArgs e)
{
// Draw your tab graphics here
Graphics g = e.Graphics;
Pen p = new Pen(Color.Blue);
g.DrawRectangle(p, myTabRect);
p = new Pen(Color.Red);
g.DrawRectangle(p, myInsideRect);
p = new Pen(Color.Green);
g.DrawRectangle(p, myOutsideRect);
}
You can draw whatever style you like into the graphic context, add text, images, etc

In C# Winforms is there a way to put dotted border around all controls and show grip points upon selection of specific controls at runtime?

I work in a team working on a IDE similar to Visual Studio to develop custom Winform code for our local clients. In our code we have User Controls overridden to make our tasks easier but most of our controls are derived from basic C# Winform Controls.
I currently need help in implementing dotted border around all our controls, with the type of grip points as provided by Visual Studio.
Unselected Controls
Selected Controls
This feature is highly demanded as it can help in aligning without compensation on visual guidelines.
We have currently implemented a dark border around all controls, using
this.BackColor = Color.Black;
this.Height = ComboBox.Height + 4;
Which puts a black border around the generated Controls, which in the above code snippet is a ComboBox.
One member pointed us towards using Margins and Padding as shown in the Microsoft documentation: https://msdn.microsoft.com/library/3z3f9e8b(v=vs.110)
But this is mostly theory and does not seem to help much. the closest thing that has come to solve this problem so far has been an online CodeProject link:
public class MyGroupBox : GroupBox
{
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
ControlPaint.DrawBorder(e.Graphics, ClientRectangle,
Color.Black, BORDER_SIZE, ButtonBorderStyle.Inset,
Color.Black, BORDER_SIZE, ButtonBorderStyle.Inset,
Color.Black, BORDER_SIZE, ButtonBorderStyle.Inset,
Color.Black, BORDER_SIZE, ButtonBorderStyle.Inset);
}
}
I am surprized to not find a close match to my search so far, perhaps i am using the wrong terminology, as I recently got into programming in this domain.
I believe that future online searches are going to be benifitted, if this problem gets solved. Looking forward for pointers form those with experience in this problem. Really appreciate any help in this direction.
I work in a team working on a IDE similar to Visual Studio ....
Developing a custom form designer is not a trivial task and needs a lot of knowledge and a lot of time and I believe the best solution which you can use, is hosting windows forms designer.
It's not just about drawing selection borders:
Each control has it's own designer with specific features, for example some controls like MenuStrip has it's own designer which enables you to add/remove items on designer.
Controls may have some specific sizing and positioning rules. For example some of them are auto-sized like TextBox or docked controls can not be reposition by mouse and so on.
Components are not visible on your form which you may need to edit them.
Some properties are design-time properties.
Some properties are added using extender providers and you need to perform additional tasks to provide a way to change them in your custom designer.
And a lot of other considerations.
Solution 1 - Hosting Windows Forms Designer
To learn more about design time architecture, take a look at Design-Time Architecture. To host windows forms designer in your application, you need to implement some interfaces like IDesignerHost, IContainer, IComponentChangeService, IExtenderProvider, ITypeDescriptorFilterService, IExtenderListService, IExtenderProviderService.
For some good examples you can take a look at:
Hosting Windows Forms Designers by Tim Dawson
Tailor Your Application by Building a Custom Forms Designer with .NET by Sayed Y. Hashimi
You may find this post useful:
Hosting Windows Forms Designer - Serialize and Deserialize designer at runtime
The post contains a working example on how to host windows forms designer at run-time and generate code:
Solution 2 - Drawing selection border over a transparent panel
While I strongly recommend using the first solution, but just for learning purpose if you want to draw selection border around controls, you can add the forms which you want to edit as a control to the host form, then put a transparent panel above the form. Handle Click event of transparent Panel and find the control under mouse position and draw a selection border around it on transparent panel like this:
In the example, I just created a transparent panel and drew selection border. It's just an example and performing sizing and positioning is out of scope of the example. It's just to show you how you can draw selection border around controls. You also can use the idea to create a SelctionBorder control and encapsulate sizing and positioning logic in the control and instead of drawing the borders, add an instance of SelectionBorder control to transparent panel and in its sizing and positioning events, change corresponding control coordinates.
Please pay attention it's just an example and in a real designer environment you should consider a lot of important things.
Transparent Panel
using System.Windows.Forms;
public class TransparentPanel : Panel
{
const int WS_EX_TRANSPARENT = 0x20;
protected override CreateParams CreateParams
{
get
{
CreateParams cp = base.CreateParams;
cp.ExStyle = cp.ExStyle | WS_EX_TRANSPARENT;
return cp;
}
}
protected override void OnPaintBackground(PaintEventArgs e)
{
}
}
Host Form
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Windows.Forms;
public partial class HostForm : Form
{
private Panel containerPanel;
private TransparentPanel transparentPanel;
private PropertyGrid propertyGrid;
public HostForm()
{
this.transparentPanel = new TransparentPanel();
this.containerPanel = new Panel();
this.propertyGrid = new PropertyGrid();
this.SuspendLayout();
this.propertyGrid.Width = 200;
this.propertyGrid.Dock = DockStyle.Right;
this.transparentPanel.Dock = System.Windows.Forms.DockStyle.Fill;
this.transparentPanel.Name = "transparentPanel";
this.containerPanel.Dock = System.Windows.Forms.DockStyle.Fill;
this.containerPanel.Name = "containerPanel";
this.ClientSize = new System.Drawing.Size(450, 210);
this.Controls.Add(this.transparentPanel);
this.Controls.Add(this.propertyGrid);
this.Controls.Add(this.containerPanel);
this.Name = "HostForm";
this.Text = "Host";
this.Load += this.HostForm_Load;
this.transparentPanel.MouseClick += this.transparentPanel_MouseClick;
this.transparentPanel.Paint += this.transparentPanel_Paint;
this.ResumeLayout(false);
}
private void HostForm_Load(object sender, EventArgs e)
{
this.ActiveControl = transparentPanel;
/**************************************/
/*Load the form which you want to edit*/
/**************************************/
var f = new Form();
f.Location = new Point(8, 8);
f.TopLevel = false;
this.containerPanel.Controls.Add(f);
SelectedObject = f;
f.Show();
}
Control selectedObject;
Control SelectedObject
{
get { return selectedObject; }
set
{
selectedObject = value;
propertyGrid.SelectedObject = value;
this.Refresh();
}
}
void transparentPanel_MouseClick(object sender, MouseEventArgs e)
{
if (this.Controls.Count == 0)
return;
SelectedObject = GetAllControls(this.containerPanel)
.Where(x => x.Visible)
.Where(x => x.Parent.RectangleToScreen(x.Bounds)
.Contains(this.transparentPanel.PointToScreen(e.Location)))
.FirstOrDefault();
this.Refresh();
}
void transparentPanel_Paint(object sender, PaintEventArgs e)
{
if (SelectedObject != null)
DrawBorder(e.Graphics, this.transparentPanel.RectangleToClient(
SelectedObject.Parent.RectangleToScreen(SelectedObject.Bounds)));
}
private IEnumerable<Control> GetAllControls(Control control)
{
var controls = control.Controls.Cast<Control>();
return controls.SelectMany(ctrl => GetAllControls(ctrl)).Concat(controls);
}
void DrawBorder(Graphics g, Rectangle r)
{
var d = 4;
r.Inflate(d, d);
ControlPaint.DrawBorder(g, r, Color.Black, ButtonBorderStyle.Dotted);
var rectangles = new List<Rectangle>();
var r1 = new Rectangle(r.Left - d, r.Top - d, 2 * d, 2 * d); rectangles.Add(r1);
r1.Offset(r.Width / 2, 0); rectangles.Add(r1);
r1.Offset(r.Width / 2, 0); rectangles.Add(r1);
r1.Offset(0, r.Height / 2); rectangles.Add(r1);
r1.Offset(0, r.Height / 2); rectangles.Add(r1);
r1.Offset(-r.Width / 2, 0); rectangles.Add(r1);
r1.Offset(-r.Width / 2, 0); rectangles.Add(r1);
r1.Offset(0, -r.Height / 2); rectangles.Add(r1);
g.FillRectangles(Brushes.White, rectangles.ToArray());
g.DrawRectangles(Pens.Black, rectangles.ToArray());
}
protected override bool ProcessTabKey(bool forward)
{
return false;
}
protected override void OnResize(EventArgs e)
{
base.OnResize(e);
this.Refresh();
}
}
Some caution would be appropriate here, modeling a UI designer after the Winforms designer is an easy decision, actually implementing it is a job that can keep you occupied for many months. Discovering that you cannot paint outside the control bounds is indeed the very first obstacle you'll run into, many more like that.
The first shortcut you might consider is to draw place-holders for the controls so you don't depend on the Control class. Works fine as long as it doesn't have to look too closely like the real control (i.e. give up on WYSIWYG) and you don't have to resize them.
But you'll surely dismiss that. You then have to do the same thing the Winforms designer does, you have to overlay a transparent window on top of the design surface. You can draw anything you want on that overlay and it provides automatic mouse and keyboard isolation so the control itself is completely oblivious of the design-time interaction. Find examples of such an overlay in this post and this post.
Last but not least, it is worth mentioning that you can leverage the existing Winforms designer in your own projects as well. You have to implement IDesignerHost. And a bunch more, unfortunately the abstraction level is fairly high and the MSDN docs rather brief. Best thing to do is to work from a sample that shows a full-featured designer. This KB article has the link. Code is excellent and well documented, you get an almost complete designer with toolbox and Properties window which de/serializes the design from/to XML and can generate C# and VB.NET code. Do look past the garish UI, it doesn't enable Visual Styles and color choices are the kind that I would make :) Making it pretty wasn't the point of the code sample.
I Have Created I windows Form Application Hope this will Help you
BackEnd C# Code
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace WindowsFormsApplication2
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
this.Paint += new PaintEventHandler(this_Paint);
}
private void this_Paint(object sender, PaintEventArgs e)
{
Pen pen = new Pen(Color.Green, 2.0F);
pen.DashStyle = DashStyle.Dash;
foreach (Control c in groupBox1.Controls)
{
e.Graphics.DrawRectangle(pen, (groupBox1.Location.X + c.Location.X)-1, (groupBox1.Location.Y + c.Location.Y)-1, c.Width + 2, c.Height + 2);
}
pen.Dispose();
}
private void Form1_Load(object sender, EventArgs e)
{
}
}
}
Designer C# Code
namespace WindowsFormsApplication2
{
partial class Form1
{
/// <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 Windows Form 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.groupBox1 = new System.Windows.Forms.GroupBox();
this.comboBox1 = new System.Windows.Forms.ComboBox();
this.comboBox2 = new System.Windows.Forms.ComboBox();
this.comboBox3 = new System.Windows.Forms.ComboBox();
this.comboBox4 = new System.Windows.Forms.ComboBox();
this.groupBox1.SuspendLayout();
this.SuspendLayout();
//
// groupBox1
//
this.groupBox1.BackColor = System.Drawing.Color.Transparent;
this.groupBox1.Controls.Add(this.comboBox4);
this.groupBox1.Controls.Add(this.comboBox3);
this.groupBox1.Controls.Add(this.comboBox2);
this.groupBox1.Controls.Add(this.comboBox1);
this.groupBox1.Location = new System.Drawing.Point(33, 36);
this.groupBox1.Name = "groupBox1";
this.groupBox1.Size = new System.Drawing.Size(193, 184);
this.groupBox1.TabIndex = 0;
this.groupBox1.TabStop = false;
this.groupBox1.Text = "groupBox1";
//
// comboBox1
//
this.comboBox1.FormattingEnabled = true;
this.comboBox1.Location = new System.Drawing.Point(36, 40);
this.comboBox1.Name = "comboBox1";
this.comboBox1.Size = new System.Drawing.Size(121, 21);
this.comboBox1.TabIndex = 0;
//
// comboBox2
//
this.comboBox2.FormattingEnabled = true;
this.comboBox2.Location = new System.Drawing.Point(36, 67);
this.comboBox2.Name = "comboBox2";
this.comboBox2.Size = new System.Drawing.Size(121, 21);
this.comboBox2.TabIndex = 1;
//
// comboBox3
//
this.comboBox3.FormattingEnabled = true;
this.comboBox3.Location = new System.Drawing.Point(36, 94);
this.comboBox3.Name = "comboBox3";
this.comboBox3.Size = new System.Drawing.Size(121, 21);
this.comboBox3.TabIndex = 1;
//
// comboBox4
//
this.comboBox4.FormattingEnabled = true;
this.comboBox4.Location = new System.Drawing.Point(36, 121);
this.comboBox4.Name = "comboBox4";
this.comboBox4.Size = new System.Drawing.Size(121, 21);
this.comboBox4.TabIndex = 1;
//
// Form1
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(284, 261);
this.Controls.Add(this.groupBox1);
this.Name = "Form1";
this.Text = "Form1";
this.Load += new System.EventHandler(this.Form1_Load);
this.groupBox1.ResumeLayout(false);
this.ResumeLayout(false);
}
#endregion
private System.Windows.Forms.GroupBox groupBox1;
private System.Windows.Forms.ComboBox comboBox1;
private System.Windows.Forms.ComboBox comboBox4;
private System.Windows.Forms.ComboBox comboBox3;
private System.Windows.Forms.ComboBox comboBox2;
}
}
Make GroupBox1 backgroundcolour 'Transparent' because I am drawing on Form not on
GroupBox
You Can also Create Border on Selected Controls by Adding if(c is ComboBox)
or if (c.Name == "comboBox1")
in foreach loop
!! Change the Color According to your Need !!
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
hatchedPen = (Pen)SystemPens.ControlDarkDark.Clone();
hatchedPen.DashStyle = System.Drawing.Drawing2D.DashStyle.Dot;
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
// Clear any existing grab handles
using (Graphics g = Graphics.FromHwnd(this.Handle))
{
foreach (Control ctrl in Controls)
{
var rect = GetGrabBounds(ctrl);
g.FillRectangle(SystemBrushes.ButtonFace, rect);
}
}
// Need to draw grab handles?
if (ActiveControl != null && e.ClipRectangle.IntersectsWith(GetGrabBounds(ActiveControl)))
{
DrawGrabHandles(ActiveControl);
}
}
private void DrawGrabHandles(Control ctrl)
{
using (Graphics g = Graphics.FromHwnd(this.Handle))
{
Rectangle bounds = GetGrabRect(ctrl);
g.DrawRectangle(hatchedPen, bounds);
foreach (Point pt in new Point[]
{
new Point(bounds.Left, bounds.Top),
new Point(bounds.Left + bounds.Width / 2, bounds.Top),
new Point(bounds.Right, bounds.Top),
new Point(bounds.Left, bounds.Top + bounds.Height / 2),
new Point(bounds.Right, bounds.Top + bounds.Height / 2),
new Point(bounds.Left, bounds.Bottom),
new Point(bounds.Left + bounds.Width / 2, bounds.Bottom),
new Point(bounds.Right, bounds.Bottom),
})
{
Rectangle r = new Rectangle(pt, new Size(5, 5));
r.X = r.X - 2;
r.Y = r.Y - 2;
g.FillRectangle(SystemBrushes.ButtonFace, r);
g.DrawRectangle(SystemPens.ControlDarkDark, r);
}
}
}
private static Rectangle GetGrabRect(Control ctrl)
{
var result = ctrl.Bounds;
result = Rectangle.Inflate(result, 4, 4);
result.X--;
result.Y--;
return result;
}
private static Rectangle GetGrabBounds(Control ctrl)
{
var result = GetGrabRect(ctrl);
result.Inflate(4, 4);
return result;
}
private Pen hatchedPen;
}

Circular RadioButton List in Windows Forms

I have designed the circular button list in web application using a jquery plugin and html. In this design user at a time select one day only like radio button list. The design is shown below:
How can I implement the the same design and functionality in windows form? Please help me, from where I am going to started to achieve this one.
There are multiple options to perform this in windows forms. As an option you can start with customizing RadioButton and Panel controls. You can create a new class derived from Panel and a new class derived from RadioButton, then override OnPaint method of those classes and draw the desired presentation.
Here is the result of a sample implementation which I shared in this post:
Custom Panel
public class MyPanel : Panel
{
public MyPanel()
{
this.Padding = new Padding(2);
}
protected override void OnPaint(PaintEventArgs e)
{
e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
using (var path = new GraphicsPath())
{
var d = this.Padding.All;
var r = this.Height - 2 * d;
path.AddArc(d, d, r, r, 90, 180);
path.AddArc(this.Width - r - d, d, r, r, -90, 180);
path.CloseFigure();
using (var pen = new Pen(Color.Silver, d))
e.Graphics.DrawPath(pen, path);
}
}
}
Custom Radio Button
public class MyRadioButton : RadioButton
{
public MyRadioButton()
{
this.Appearance = System.Windows.Forms.Appearance.Button;
this.BackColor = Color.Transparent;
this.TextAlign = ContentAlignment.MiddleCenter;
this.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
this.FlatAppearance.BorderColor = Color.RoyalBlue;
this.FlatAppearance.BorderSize = 2;
}
protected override void OnPaint(PaintEventArgs e)
{
this.OnPaintBackground(e);
using (var path = new GraphicsPath())
{
var c = e.Graphics.ClipBounds;
var r = this.ClientRectangle;
r.Inflate(-FlatAppearance.BorderSize, -FlatAppearance.BorderSize);
path.AddEllipse(r);
e.Graphics.SetClip(path);
base.OnPaint(e);
e.Graphics.SetClip(c);
e.Graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
if (this.Checked)
{
using (var p = new Pen(FlatAppearance.BorderColor,
FlatAppearance.BorderSize))
{
e.Graphics.DrawEllipse(p, r);
}
}
}
}
}
Required usings
using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;
Hii Gopal you can achive this functionality using custom control.Goodluck
please refer these links
How to customize Button Control like this one?
https://msdn.microsoft.com/en-us/library/h4te2zh2(v=vs.90).aspx
How do I create button with rounded corners/edges on Winform C#?
You can start with a list view or list box
Change the item template as a button and add style to your button as you need.
Make the selection mode of the list view as Single.

C# vertical tab control with horizontal text

I want to display some information in tab control This is what I want.
I have used the methods that I found on your side to make changes in properties of tab control and using Draw Event but the output is not like what i needed.The output comes likeThis is what I am getting..
I want the text to be horizontal. Also my VS is 2008
I followed these instructions in VB and converted them to C#. Worked for me. Basically in tab control properties set the following:
Alignment = Left
SizeMode = Fixed
ItemSize = 30, 120: Width = 30 Height = 120
DrawMode = OwnerDrawFixed
Then you have to handle DrawItem event like that:
private void tabControl1_DrawItem(object sender, DrawItemEventArgs e)
{
var g = e.Graphics;
var text = this.tabControl1.TabPages[e.Index].Text;
var sizeText = g.MeasureString(text, this.tabControl1.Font);
var x = e.Bounds.Left + 3;
var y = e.Bounds.Top + (e.Bounds.Height - sizeText.Height) / 2;
g.DrawString(text, this.tabControl1.Font, Brushes.Black, x, y);
}
And the result is:
Set the SizeMode property to Fixed, so that all tabs are the same width.
Set the ItemSize property to the preferred fixed size for the tabs. Keep in mind that the ItemSize property behaves as though the tabs were on top, although they are left-aligned. As a result, in order to make the tabs wider, you must change the Height property, and in order to make them taller, you must change the Width property. [I set the ItemSize as: 25, 150].
Set the DrawMode property to OwnerDrawFixed.
private void tabControl1_DrawItem(object sender, DrawItemEventArgs e)
{
Graphics g = e.Graphics;
Brush _textBrush;
// Get the item from the collection.
TabPage _tabPage = tabControl1.TabPages[e.Index];
// Get the real bounds for the tab rectangle.
Rectangle _tabBounds = tabControl1.GetTabRect(e.Index);
if (e.State == DrawItemState.Selected)
{
// Draw a different background color, and don't paint a focus rectangle.
_textBrush = new SolidBrush(Color.Red);
g.FillRectangle(Brushes.Gray, e.Bounds);
}
else
{
_textBrush = new System.Drawing.SolidBrush(e.ForeColor);
e.DrawBackground();
}
// Use our own font.
Font _tabFont = new Font("Arial", (float)10.0, FontStyle.Bold, GraphicsUnit.Pixel);
// Draw string. Center the text.
StringFormat _stringFlags = new StringFormat();
_stringFlags.Alignment = StringAlignment.Center;
_stringFlags.LineAlignment = StringAlignment.Center;
g.DrawString(_tabPage.Text, _tabFont, _textBrush, _tabBounds, new StringFormat(_stringFlags));
}

C# Win Forms tab Control tab width error

I have custom tab control where OnPaint method is override.
Then strange growth of tabs occurs. Tabs getting bigger (padding getting bigger) and they width depends on length of the text.
When I use default Tab Control - padding is OK. How to avoid this situation when I use UserPaint?
partial class Tab : TabControl
{
public Tab()
{
InitializeComponent();
Init();
}
private void Init()
{
this.SetStyle(ControlStyles.UserPaint, true);
this.SetStyle(ControlStyles.AllPaintingInWmPaint, true);
this.SetStyle(ControlStyles.DoubleBuffer, true);
this.SetStyle(ControlStyles.ResizeRedraw, true);
}
protected override void OnPaint(PaintEventArgs e)
{
DrawTabPane(e.Graphics);
}
private void DrawTabPane(Graphics g)
{
if (!Visible)
return;
// here we draw our tabs
for (int i = 0; i < this.TabCount; i++)
DrawTab(g, this.TabPages[i], i);
}
internal void DrawTab(Graphics g, TabPage tabPage, int nIndex)
{
Rectangle recBounds = this.GetTabRect(nIndex);
RectangleF tabTextArea = recBounds;
Point[] pt = new Point[4];
pt[0] = new Point(recBounds.Left + 1, recBounds.Bottom);
pt[1] = new Point(recBounds.Left + 1, recBounds.Top + 1);
pt[2] = new Point(recBounds.Right - 1, recBounds.Top + 1);
pt[3] = new Point(recBounds.Right - 1, recBounds.Bottom);
Brush br = new SolidBrush(clr_tab_norm);
g.FillPolygon(br, pt);
br.Dispose();
StringFormat stringFormat = new StringFormat();
stringFormat.Alignment = StringAlignment.Center;
stringFormat.LineAlignment = StringAlignment.Center;
br = new SolidBrush(clr_txt);
g.DrawString(tabPage.Text, Font, br, tabTextArea, stringFormat);
}
}
Turning on ControlStyles.UserPaint for controls that are built into Windows, like TabControl, is not the proper thing to do. I assume the bug is in GetTabRect(), it isn't visible in the snippet.
Instead, you should use the TabControl.DrawMode property and implement the DrawItem event. There's a good example in the MSDN Library.
It would appear from the image that your code is setting the size of the tabs to be wider than they need to be. The extra padding is present in all your tabs but it is just more visible in the tabs with longer text.
I can't be sure why this is but I'd guess that the code to calculate the size of the tabs (based on font metrics) is using a different font from that used to draw the tabs.

Categories