.NET Draw Rounded Corners On A Rectangle

Shows how to use the GDI+ Bitmap, Rectangle, GraphicsPath, and the .AddArc method to draw rounded corners on an image.

GDI+ without training and a really good book or two is like being handed all the tools necessary to build a rocket and not being shown how.  Tons of capability with absolutely no clue (or useful documentation from Mr. Bill Gates) how to use it.  You can imagine my journey over the last few days after being asked to incorporate highly flexbile color branding to a rather powerful survey web site tool I've been working on.

Essentially, our marketing department wants to have user settings for changing button background color, button color, button gradient fade color, text color, etc... for every possible hex color combination.  Plus, they want rounded rectangles similar to XP for buttons and special text sections inside color filled rounded text areas.  Knowing just how flexible this would need to be, I opted to put together a branding class to draw the graphics at runtime.  Plus, I also whipped up an http handler page to draw the objects and use the image url as a unique key to cache the images if they haven't been drawn yet.

Here's a sample of a rounded rectangle.  This has no fancy formatting or effects.  I'll be posting tips in the near future regarding the use of textures and gradients.



The following code sample is a small excerpt from my project.  It is also well suited for implementation in our desktop tools that will soon require similar functionality.  The drawing strategy I used was to draw an arc for all 4 corners, place a rectangle in the middle, and use the GraphicsPath object to meld them together and fill the contents.  Let's check out the code below:

// How To Call Method

// Not all of the properties shown here are actually used in the sample
//  method.  I have many methods for drawing other shapes and effects not
//  shown here.

var ImgSetting = new Branding.Images.Settings();

ImgSetting.BackGroundColor = Color.White;
ImgSetting.BackColor = ColorTranslator.FromHtml("#6699ff");
ImgSetting.ForeColor = Color.Black;
ImgSetting.TextColor = Color.White;
ImgSetting.Height = 30;
ImgSetting.Width = 190;
ImgSetting.Text = "Save And Return Later";
ImgSetting.TextFont = new Font("Arial",10,FontStyle.Bold,GraphicsUnit.Point);
ImgSetting.ApplyGlass = false;
ImgSetting.ApplyBorder = true;
ImgSetting.ApplyInternalFill = true;
ImgSetting.BorderColor = ColorTranslator.FromHtml("#000000");

var oTest = Branding.Images.GetRoundedRectangle(ImgSetting);  

this.pictureBox1.Image = oTest;


// Method Source Code

using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.Drawing.Text;  
using Branding;

namespace Branding
{

    public class Images
    {

         public Images()
        {

        }

     #region Settings
    public struct Settings
    {
       public Color BackGroundColor;
      public Color BackColor;
      public Color ForeColor;
      public Color TextColor;
      public Color BorderColor;
      public Font  TextFont;
      public string Text;
      public int Width;
      public int Height;
      public bool ApplyGlass;
      public bool ApplyInternalFill;
      public bool ApplyBorder;
    }
   #endregion

   #region Get Rounded Rectangle
   public static Bitmap GetRoundedRectangle(Branding.Images.Settings ImageSetting)
   {

     // Define the size of the rectangle used for each of the 4 arcs.

     Size CornerSize = new Size(10,10);

     //  Define the buffer between the rounded rectangle and the image itself.

     int x = 5;
     int y = 5;

     // Set positions for the arcs and center positions for the rectangle

     int w = ImageSetting.Width;
     int h = ImageSetting.Height;
     int xr = Convert.ToInt32(w - (x + CornerSize.Width));
     int yr = Convert.ToInt32(h - (y + CornerSize.Height));
     int iw = Convert.ToInt32(w - xr);
     int ih = Convert.ToInt32(h - yr);

     // Standard angle for each arc.

     float sweepAngle = 90.0f;

     Bitmap bm = null;

     SolidBrush fillBrush;
     Color FillColor;

       // Create a new bitmap from scratch

       bm = new Bitmap(w,h,PixelFormat.Format32bppArgb);

       Graphics g = Graphics.FromImage(bm);

       // Give us fairly high line quality.

      g.SmoothingMode  = SmoothingMode.AntiAlias;

       // Create a rectangle for the background of the image

       Rectangle bgImg = new Rectangle(0,0,w,h);
            
      // Fill the entire image with our image background color

      SolidBrush bgBrush = new SolidBrush(ImageSetting.BackGroundColor);

       g.FillRectangle(bgBrush,bgImg);

       bgBrush.Dispose();

       // Create 4 10 x 10 rectangles for our arcs to fit in. tl=topleft, br=bottomright

       Rectangle tl = new Rectangle(x,y,CornerSize.Width,CornerSize.Height);
       Rectangle tr = new Rectangle(xr,y,CornerSize.Width,CornerSize.Height);
       Rectangle bl = new Rectangle(x,yr,CornerSize.Width,CornerSize.Height);
       Rectangle br = new Rectangle(xr,yr,CornerSize.Width,CornerSize.Height);

       // Create an inner rectangle to fill the middle
      
       Rectangle innerRect = new Rectangle(x,CornerSize.Width,x + xr,yr - y);

       // Here's how it is all bound together.  We need to add the arcs and the
      // inner rectangle to a single GraphicsPath object.  This allows us to call
      // the .FillPath method to only fill in the section inside the arcs and our rectangle.

       GraphicsPath path = new System.Drawing.Drawing2D.GraphicsPath();

       path.AddArc(tl, 180, sweepAngle);
       path.AddArc(tr, 270, sweepAngle);
       path.AddRectangle(innerRect);  
       path.AddArc(bl, 90, sweepAngle);
       path.AddArc(br, 360, sweepAngle);
            
       if (ImageSetting.ApplyInternalFill)
       {
         FillColor = ImageSetting.BackColor;
       }
       else
       {
         FillColor = ImageSetting.BackGroundColor;
       }

       fillBrush = new SolidBrush(FillColor);

       // Connect all border lines drawn in the entire path

       path.CloseAllFigures();

       g.FillPath(fillBrush,path);

       fillBrush.Dispose();

       if (ImageSetting.ApplyBorder)
       {
           // If we want a border, we'll need draw the path first and then
         // draw a horizontal line at the top of our inner rectangle and again
         // at the bottom with the same color as our rectangle fill color to hide
         // extra lines drawn above in the path.CloseAllFigures() method.

         Pen borderPen = new Pen(ImageSetting.BorderColor, 1);
         Pen fillPen = new Pen(FillColor, 1);

         g.DrawPath(borderPen,path);
         g.DrawLine(fillPen,innerRect.Left + 1,innerRect.Top,innerRect.Width + (x-1),innerRect.Top);
         g.DrawLine(fillPen,innerRect.Left + 1,innerRect.Bottom,innerRect.Width + (x-1),innerRect.Bottom);
    
         fillPen.Dispose();
         borderPen.Dispose();

      }


      if (ImageSetting.Text.Length > 0)
      {

        // If our image has text, draw it using the Font passed in and center the
       // text vertically and horizontally against our innerRect rectangle.

        SolidBrush TextBrush = new SolidBrush(ImageSetting.TextColor);

        StringFormat sf = new StringFormat();

        sf.Alignment = StringAlignment.Center;

        sf.Trimming = StringTrimming.EllipsisCharacter;

        sf.LineAlignment = StringAlignment.Center;

        g.TextRenderingHint = TextRenderingHint.AntiAliasGridFit;

        g.DrawString(ImageSetting.Text,ImageSetting.TextFont,TextBrush,innerRect,sf);

        TextBrush.Dispose();

        sf.Dispose();

      }

      g.Dispose();

    return bm;
   }
   #endregion

  }
}
}

By Robbe Morris   Popularity  (6198 Views)
Picture
Biography - Robbe Morris
Robbe has been a Microsoft MVP in C# since 2004. He is also the co-founder of NullSkull.com which provides .NET articles, book reviews, software reviews, and software download and purchase advice.  Robbe also loves to scuba dive and go deep sea fishing in the Florida Keys or off the coast of Daytona Beach. Microsoft MVP