Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Extended Graphics - An implementation of Rounded Rectangle in C#

0.00/5 (No votes)
13 Dec 2003 3  
An implementation of Rounded Rectangle in C# which could be used to create XP-like buttons and rounded border.

Introduction

Devasted with hours of coding practices and hard-to-design Java applications, I was tempted to try the Microsoft .Net Framework and found C# as a better alternative to Java. Making Windows programs was even easier and I could see endless possibilities for programs on the Windows Platform.

After-effects of this switch left me wondering how I could create graphics on components and forms and I somehow (read: hours of searches on CodeProject and the Framework Documentation) came to the conclusion that the use of Graphics was a necessity in my programs. Meanwhile, CodeProject was flooding with articles on creating XP-like buttons and forms. More specifically, user controls that used rounded paths and ellaborate designs. That's when I decided that I would create a similar button with a Rounded Rectangle. "It's so simple", I thought to myself.

Preliminaries

I sat down to create the button myself. Vague images, floated around in my head, of how I would have accomplished this in Java. The most appropriate implementation that I could think of was something like this (Oh! How I loved Java):

import java.awt.*;

// A simple implementation of a drawing with Rounded Rectangle in Java.

// Notice that the Graphics class has methods:

// fillRoundRect and drawRoundRect,

// both having six arguments, the last two being width 

// and height of the round

// curves or arcs.

public class RoundButton extends Canvas
{
    public RoundButton()
    {
        // Initialization code comes here

        this.setSize(100, 20);
        this.repaint();
    }

    public void paint(Graphics g)
    {
        // Drawing code comes here

        g.setColor(new Color(200, 200, 200));
        g.fillRoundRect(2, 2, this.getWidth()-4,
          this.getHeight()-4, 5, 5);
        g.setColor(new Color(60, 60, 60));
        g.drawRoundRect(2, 2, this.getWidth()-4,
          this.getHeight()-4, 5, 5);
    }
}

The Horror

However, as I stepped further to demonstrate the same effect in C#, it was then that I came face-to-face with sudden disbelief. The C# Graphics class that I had praised so much lacked a method for a Rounded Rectangle. What!?! How tedious would it be to create a Rounded Rectangle in such a situation. So, I retreated back to where I had started: searching CodeProject. Though, I found loads of code telling me how this could be implemented but all the codes were built around some user control or component. I wanted a class that would most probably inherit the methods and properties of the Graphics class and include additional and extended functionality. But there was still one problem: The Graphics class was abstract and/or sealed (I don't know why I hate this word).

The Answers

Amidst results on Google [new window], I came across a forum named Drawing Rectangle but with rounded corners... [new window] that lay there, waiting for a desparate soul to encounter it's existence on the Mathew Reynold's .NET 247's Newsgroup [new window]. A certain person, whom I would like to mention here, named Tim Overbay, provided with a little routine to create such a Rounded Rectangle in VB.Net. This was just the starting point I had needed to carry on with my very own implementation. I copied the code, converted it to C# and there I had it. The final result: a class that I now lovingly dub as the ExtendedGraphics class. The complete code is present here as follows.

using System; 
using System.Drawing; 
using System.Drawing.Drawing2D; 

// A simple extension to the Graphics class for extended 

// graphic routines, such, 

// as for creating rounded rectangles. 

// Because, Graphics class is an abstract class, 

// that is why it can not be inherited. Although, 

// I have provided a simple constructor 

// that builds the ExtendedGraphics object around a 

// previously created Graphics object. 

// Please contact: aaronreginald@yahoo.com for the most 

// recent implementations of

// this class. 

namespace System.Drawing.Extended 
{ 

    /// <SUMMARY> 

    /// Inherited child for the class Graphics encapsulating 

    /// additional functionality for curves and rounded rectangles. 

    /// </SUMMARY> 

    public class ExtendedGraphics 
    { 

        private Graphics mGraphics; 
        public Graphics Graphics 
        { 
            get{ return this.mGraphics; } 
            set{ this.mGraphics = value; } 
        } 


        public ExtendedGraphics(Graphics graphics) 
        { 
            this.Graphics = graphics; 
        } 


        #region Fills a Rounded Rectangle with integers. 
        public void FillRoundRectangle(System.Drawing.Brush brush, 
          int x, int y,
          int width, int height, int radius) 
        { 

            float fx = Convert.ToSingle(x);
            float fy = Convert.ToSingle(y); 
            float fwidth = Convert.ToSingle(width);
            float fheight = Convert.ToSingle(height); 
            float fradius = Convert.ToSingle(radius); 
            this.FillRoundRectangle(brush, fx, fy, 
              fwidth, fheight, fradius); 

        } 
        #endregion 


        #region Fills a Rounded Rectangle with continuous numbers.
        public void FillRoundRectangle(System.Drawing.Brush brush, 
          float x, float y,
          float width, float height, float radius)
        {
            RectangleF rectangle = new RectangleF(x, y, width, height);
            GraphicsPath path = this.GetRoundedRect(rectangle, radius);
            this.Graphics.FillPath(brush, path);
        } 
        #endregion


        #region Draws a Rounded Rectangle border with integers. 
        public void DrawRoundRectangle(System.Drawing.Pen pen, int x, int y,
          int width, int height, int radius) 
        { 
            float fx = Convert.ToSingle(x);
            float fy = Convert.ToSingle(y); 
            float fwidth = Convert.ToSingle(width);
            float fheight = Convert.ToSingle(height); 
            float fradius = Convert.ToSingle(radius); 
            this.DrawRoundRectangle(pen, fx, fy, fwidth, fheight, fradius); 
        }
        #endregion 


        #region Draws a Rounded Rectangle border with continuous numbers. 
        public void DrawRoundRectangle(System.Drawing.Pen pen, 
          float x, float y,
          float width, float height, float radius) 
        { 
            RectangleF rectangle = new RectangleF(x, y, width, height); 
            GraphicsPath path = this.GetRoundedRect(rectangle, radius); 
            this.Graphics.DrawPath(pen, path); 
        } 
        #endregion 


        #region Get the desired Rounded Rectangle path. 
        private GraphicsPath GetRoundedRect(RectangleF baseRect, 
           float radius) 
        {
            // if corner radius is less than or equal to zero, 

            // return the original rectangle 

            if( radius<=0.0F ) 
            { 
                GraphicsPath mPath = new GraphicsPath(); 
                mPath.AddRectangle(baseRect); 
                mPath.CloseFigure(); 
                return mPath;
            }

            // if the corner radius is greater than or equal to 

            // half the width, or height (whichever is shorter) 

            // then return a capsule instead of a lozenge 

            if( radius>=(Math.Min(baseRect.Width, baseRect.Height))/2.0) 
              return GetCapsule( baseRect ); 

            // create the arc for the rectangle sides and declare 

            // a graphics path object for the drawing 

            float diameter = radius * 2.0F; 
            SizeF sizeF = new SizeF( diameter, diameter );
            RectangleF arc = new RectangleF( baseRect.Location, sizeF ); 
            GraphicsPath path = new System.Drawing.Drawing2D.GraphicsPath(); 

            // top left arc 

            path.AddArc( arc, 180, 90 ); 

            // top right arc 

            arc.X = baseRect.Right-diameter; 
            path.AddArc( arc, 270, 90 ); 

            // bottom right arc 

            arc.Y = baseRect.Bottom-diameter; 
            path.AddArc( arc, 0, 90 ); 

            // bottom left arc

            arc.X = baseRect.Left;     
            path.AddArc( arc, 90, 90 );     

            path.CloseFigure(); 
            return path; 
        } 
        #endregion 

        #region Gets the desired Capsular path. 
        private GraphicsPath GetCapsule( RectangleF baseRect ) 
        { 
            float diameter; 
            RectangleF arc; 
            GraphicsPath path = new System.Drawing.Drawing2D.GraphicsPath(); 
            try 
            { 
                if( baseRect.Width>baseRect.Height ) 
                { 
                    // return horizontal capsule 

                    diameter = baseRect.Height; 
                    SizeF sizeF = new SizeF(diameter, diameter);
                    arc = new RectangleF( baseRect.Location, sizeF ); 
                    path.AddArc( arc, 90, 180); 
                    arc.X = baseRect.Right-diameter; 
                    path.AddArc( arc, 270, 180); 
                } 
                else if( baseRect.Width < baseRect.Height ) 
                { 
                    // return vertical capsule 

                    diameter = baseRect.Width;
                    SizeF sizeF = new SizeF(diameter, diameter);
                    arc = new RectangleF( baseRect.Location, sizeF ); 
                    path.AddArc( arc, 180, 180 ); 
                    arc.Y = baseRect.Bottom-diameter; 
                    path.AddArc( arc, 0, 180 ); 
                } 
                else
                { 
                    // return circle 

                    path.AddEllipse( baseRect ); 
                }
            } 
            catch(Exception ex)
            {
                path.AddEllipse( baseRect ); 
            } 
            finally 
            { 
                path.CloseFigure(); 
            } 
            return path; 
        } 
        #endregion 
    } 
} 

What does the code do?

The above code is a simple class that could be used in your projects for additional drawing routines. What I tried to do here is that I created the class that accepts a Graphics class object. Why I did this is a harsher punishment I got because the Graphics class was sealed and it could not be inherited. Otherwise, I would have simply had inherited my class with the Graphics class.

There are two private methods in the class and four public methods. The two private methods namely the GetRoundedRect(...) and the GetCapsule(...) method are of the most importance. Both these methods draw four lines connected with rounded arcs to form a Rounded Rectangle. If the rectangle's height or width is less than the diameter of arc specified, then the method instead of providing the user with a rectangular shape, gives the user a capsule obtained through the GetCapsule(...) method. If both the width and height are less than the diameter or the arc and are of the same size, then a circular ellipse is obtained from the method.

To make the class more useful and familiar, four methods are added to the class that resemble methods found in the Graphics class.

What's Next...

Think of this article as a first in a series of articles, because I would be using this same class in some of my upcoming tutorials on how to create buttons and effective user interface. I would be grateful to anyone who gives me advice on how I could optimize the above code and any suggestions. I am working on an implementation for even-sided polygons and will include those snippets and methods in this class in the near future.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here