Click here to Skip to main content
15,888,351 members
Articles / Programming Languages / C#

Button with Rounded Edges C#

Rate me:
Please Sign up or sign in to vote.
3.10/5 (9 votes)
28 Jan 2019CPOL2 min read 66.7K   4.3K   12   16
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!

Image 1

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."
    Properties on Mouse Click:
    • 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":
    C#
    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".

    C#
    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":
    C#
    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.

    C#
    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.

    C#
    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:

  1. Add file to your project
  2. Drag and Drop the class to Toolbox
  3. Rebuild
  4. Drag and Drop "RoundedButton" from Toolbox to your Control
  5. 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.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Spain Spain
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
QuestionButton click event is not firiing Pin
Fawmy Fazlan30-Mar-23 0:45
Fawmy Fazlan30-Mar-23 0:45 
QuestionRounded Button Pin
Member 1190867219-Mar-23 4:31
Member 1190867219-Mar-23 4:31 
PraiseRe: Not useful, etc. Pin
JunkyDog28-Oct-21 10:24
JunkyDog28-Oct-21 10:24 
QuestionRoundedButton contains a bug Pin
Member 119048894-May-21 5:33
Member 119048894-May-21 5:33 
QuestionHow to fill the corner with this logic? Pin
Member 1492336126-Aug-20 19:46
Member 1492336126-Aug-20 19:46 
QuestionEliptic Button Full class with gradient Background Pin
han6man10-Apr-20 16:34
professionalhan6man10-Apr-20 16:34 
QuestionRound Eliptic Button Pin
han6man10-Apr-20 5:58
professionalhan6man10-Apr-20 5:58 
QuestionSome changes and additives Pin
han6man10-Apr-20 5:51
professionalhan6man10-Apr-20 5:51 
GeneralStrange behavior Pin
Member 1413428930-Jan-19 19:15
Member 1413428930-Jan-19 19:15 
GeneralRe: Strange behavior Pin
han6man10-Apr-20 6:02
professionalhan6man10-Apr-20 6:02 
General[My vote of 1] Not useful and memory leaks Pin
Rene Balvert29-Jan-19 1:27
Rene Balvert29-Jan-19 1:27 
In my opinion there are many and better articles about round/glass buttons and other round/shape controls with pretty cool effects.

And you are leaking memory by not disposing, especially in paint events you should be very careful.

Read some articles about GDI (GDI+ tutorial for beginners) and by experimenting you could create much nicer buttons with glossy shining effects.
GeneralRe: [My vote of 1] Not useful and memory leaks Pin
Ralf Meier30-Jan-19 3:25
mveRalf Meier30-Jan-19 3:25 
GeneralRe: [My vote of 1] Not useful and memory leaks Pin
Rene Balvert30-Jan-19 3:48
Rene Balvert30-Jan-19 3:48 
GeneralRe: [My vote of 1] Not useful and memory leaks Pin
Ralf Meier30-Jan-19 21:19
mveRalf Meier30-Jan-19 21:19 
GeneralRe: [My vote of 1] Not useful and memory leaks Pin
han6man10-Apr-20 6:04
professionalhan6man10-Apr-20 6:04 
GeneralRe: [My vote of 1] Not useful and memory leaks Pin
han6man10-Apr-20 6:06
professionalhan6man10-Apr-20 6:06 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.