Button with Rounded Edges C#
A Windows customizable buttom with rounded edges. It's supports the designer support.
Introduction
Some time ago, I tried to find a nice round-border-button control. But I couldn't find one that fit my needs, so like a good bored programmer, I decided to write my own. I "almost" finished it, I guess that it can have more features but it works for my particular case, so I decided to make use of that tradition that says "if it works, just do not touch it".
Though I say it myself, I think the buttons look nice - you'll have to judge for yourself!
How It Works
My class "RoundedButton
" which inherits from "System.Windows.Forms.Button
" contains a list of new properties that let customize the look of the button and it has some methods overridden.
New Properties:
BorderRadius
" - how far is the edge of the button rounded."BorderWidth
" - specifies the width of edge."BorderColor
" - specifies the color of edge."- Properties on Mouse Over:
BorderOverWidth
" - specifies the width of edge when mouse is over button."BorderOverColor
" - specifies the color of edge when mouse is over button."
BorderDownWidth
" - specifies the width of edge when button triggers the event "OnMouseDown
""BorderDownColor
" - specifies the color of edge when button triggers the event "OnMouseDown
""
Additionally, the inherited properties "BackColor
", "FlatAppearance.MouseDownBackColor
" or FlatAppearance.MouseOverBackColor
" can be used to specify the Back Color in the possible states of button.
Noteworthy Sections of Code
There are three methods that are worth showing:
- In the first place "
GetRoundPath
":GraphicsPath GetRoundPath(RectangleF Rect, int radius, float width)
This method returns a
GraphicsPath
that fits in "Rect
" with the specified "radius
" considering that the thickness of the edge is "width
".GraphicsPath GetRoundPath(RectangleF Rect, int radius, float width) { //Fix radius to rect size radius = (int) Math.Max( ( Math.Min(radius, Math.Min(Rect.Width, Rect.Height)) - width),1); float r2 = radius / 2f; float w2 = width / 2f; GraphicsPath GraphPath = new GraphicsPath(); //Top-Left Arc GraphPath.AddArc(Rect.X + w2, Rect.Y + w2, radius, radius, 180, 90); //Top-Right Arc GraphPath.AddArc(Rect.X + Rect.Width - radius - w2, Rect.Y + w2, radius, radius, 270, 90); //Bottom-Right Arc GraphPath.AddArc(Rect.X + Rect.Width - w2 - radius, Rect.Y + Rect.Height - w2 - radius, radius, radius, 0, 90); //Bottom-Left Arc GraphPath.AddArc(Rect.X + w2, Rect.Y - w2 + Rect.Height - radius, radius, radius, 90, 90); //Close line ( Left) GraphPath.AddLine(Rect.X + w2, Rect.Y + Rect.Height - r2 - w2, Rect.X + w2,Rect.Y + r2 + w2); return GraphPath; }
In the first place, I used the method of
GraphicsPath
"CloseFigure()
", but the behavior was far away than I expected. A measure that increased the thickness of the edge was closer to the center, I don't know the reason. - In the second place "
DrawText
":private void DrawText(Graphics g,RectangleF Rect)
When I tried to draw the back color by
base.OnPaint()
it didn't work as I expected, so I decided to draw the back color by myself but then I was forced to write this method with the goal of drawing the text inside of button.private void DrawText(Graphics g,RectangleF Rect) { float r2 = BorderRadius / 4f; float w2 = BorderWidth / 2f; Point point = new Point(); StringFormat format = new StringFormat(); switch (TextAlign) { case ContentAlignment.TopLeft: point.X = (int)(Rect.X + r2/2 + w2 + Padding.Left); point.Y = (int)(Rect.Y + r2/2 + w2 + Padding.Top); format.LineAlignment = StringAlignment.Center; break; case ContentAlignment.TopCenter: point.X = (int)(Rect.X + Rect.Width/2f); point.Y = (int)(Rect.Y + r2/2 + w2 + Padding.Top); format.LineAlignment = StringAlignment.Center; format.Alignment = StringAlignment.Center; break; case ContentAlignment.TopRight: point.X = (int)(Rect.X + Rect.Width - r2/2 - w2 - Padding.Right); point.Y = (int)(Rect.Y + r2 / 2 + w2 + Padding.Top); format.LineAlignment = StringAlignment.Center; format.Alignment = StringAlignment.Far; break; case ContentAlignment.MiddleLeft: point.X = (int)(Rect.X + r2 / 2 + w2 + Padding.Left); point.Y = (int)(Rect.Y + Rect.Height/2); format.LineAlignment = StringAlignment.Center; break; case ContentAlignment.MiddleCenter: point.X = (int)(Rect.X +Rect.Width / 2); point.Y = (int)(Rect.Y + Rect.Height / 2); format.LineAlignment = StringAlignment.Center; format.Alignment = StringAlignment.Center; break; case ContentAlignment.MiddleRight: point.X = (int)(Rect.X + Rect.Width - r2 / 2 - w2 - Padding.Right); point.Y = (int)(Rect.Y + Rect.Height / 2); format.LineAlignment = StringAlignment.Center; format.Alignment = StringAlignment.Far; break; case ContentAlignment.BottomLeft: point.X = (int)(Rect.X + r2 / 2 + w2 + Padding.Left); point.Y = (int)(Rect.Y + Rect.Height - r2 / 2 - w2 - Padding.Bottom); format.LineAlignment = StringAlignment.Center; break; case ContentAlignment.BottomCenter: point.X = (int)(Rect.X + Rect.Width/2); point.Y = (int)(Rect.Y + Rect.Height - r2 / 2 - w2 - Padding.Bottom); format.LineAlignment = StringAlignment.Center; format.Alignment = StringAlignment.Center; break; case ContentAlignment.BottomRight: point.X = (int)(Rect.X + Rect.Width - r2 / 2 - w2 - Padding.Right); point.Y = (int)(Rect.Y + Rect.Height - r2 / 2 - w2 - Padding.Bottom); format.LineAlignment = StringAlignment.Center; format.Alignment = StringAlignment.Far; break; default: break; } /* Debug using (Pen pen = new Pen(Color.Black, 1)) { g.DrawLine(pen, new Point(0, 0), point); g.DrawLine(pen, point.X, 0, point.X, point.Y); g.DrawLine(pen, 0, point.Y, point.X, point.Y); } */ using (Brush brush = new SolidBrush(ForeColor)) g.DrawString(Text, Font, brush, point, format); }
- In Last place and most important "
OnPaint
":This method is overridden from
System.Windows.Forms.Button
and it is responsible for drawing the button.protected override void OnPaint(PaintEventArgs e) { e.Graphics.SmoothingMode = SmoothingMode.HighQuality; RectangleF Rect = new RectangleF(0, 0, this.Width, this.Height); Brush brush = new SolidBrush(this.BackColor); GraphicsPath GraphPath = GetRoundPath(Rect, BorderRadius); this.Region = new Region(GraphPath); //Draw Back Color if(IsMouseDown && !FlatAppearance.MouseDownBackColor.IsEmpty) using (Brush mouseDownBrush = new SolidBrush(FlatAppearance.MouseDownBackColor)) e.Graphics.FillPath(mouseDownBrush, GraphPath); else if (IsHovered && !FlatAppearance.MouseOverBackColor.IsEmpty) using (Brush overBrush = new SolidBrush(FlatAppearance.MouseOverBackColor)) e.Graphics.FillPath(overBrush, GraphPath); else e.Graphics.FillPath(brush, GraphPath); //Draw Border #region DrawBorder GraphicsPath GraphInnerPath; Pen pen; if (IsMouseDown && !BorderDownColor.IsEmpty) { GraphInnerPath = GetRoundPath(Rect, BorderRadius, BorderDownWidth); pen = new Pen(BorderDownColor, BorderDownWidth); } else if (IsHovered && !BorderOverColor.IsEmpty) { GraphInnerPath = GetRoundPath(Rect, BorderRadius, BorderOverWidth); pen = new Pen(BorderOverColor, BorderOverWidth); } else { GraphInnerPath = GetRoundPath(Rect, BorderRadius, BorderWidth); pen = new Pen(BorderColor, BorderWidth); } pen.Alignment = PenAlignment.Inset; if(pen.Width>0) e.Graphics.DrawPath(pen, GraphInnerPath); #endregion //Draw Text DrawText(e.Graphics,Rect); }
How to Use
Using Visual Studio:
- Add file to your project
- Drag and Drop the class to Toolbox
- Rebuild
- Drag and Drop "
RoundedButton
" fromToolbox
to your Control - Customize it on Properties panel
If you want use it by code, it's the same as a button from System.Windows.Form
, just use the Properties included in RoundedButton
to customize it.