Click here to Skip to main content
15,867,985 members
Articles / Programming Languages / Java
Article

Extended Graphics - An implementation of Rounded Rectangle in C#

Rate me:
Please Sign up or sign in to vote.
4.52/5 (36 votes)
13 Dec 2003CC (ASA 2.5)4 min read 451.3K   87   47
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):

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

C#
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, along with any associated source code and files, is licensed under The Creative Commons Attribution-ShareAlike 2.5 License


Written By
Software Developer (Senior) Plasmoid Technologies
United Kingdom United Kingdom
Arun Reginald Zaheeruddin is a software developer residing in London. His initial forays into the world of software development began in December 1999 when he first got hooked to Java. He still prefers it over other programming languages. Also with a commercial experience of over eight years in C, C++, C#, Objective-C, VB.Net, Python, Ruby, PHP, HTML, Javascript and CSS, he feels it an honour to share his knowledge about the world of programming with people thinking of wading its waters. In his part time, he does laptop repairs at the Computer & Laptop Centre.

Comments and Discussions

 
QuestionGood Solution For My Project. Pin
Ehsaan.Crimson1-Jul-15 21:07
Ehsaan.Crimson1-Jul-15 21:07 
GeneralMy vote of 3 Pin
A.Q.Ghouri28-Apr-14 18:11
A.Q.Ghouri28-Apr-14 18:11 
GeneralMy vote of 5 Pin
Hanson Cheng12-Apr-11 11:36
Hanson Cheng12-Apr-11 11:36 
GeneralRadius calculation Pin
MaxAnarki16-Jul-09 23:08
MaxAnarki16-Jul-09 23:08 
GeneralRe: Radius calculation Pin
Arun Reginald Zaheeruddin24-Jul-09 19:25
Arun Reginald Zaheeruddin24-Jul-09 19:25 
GeneralI ported it for CompactFramework on Windows Mobile Pin
Daniel Siegl21-Nov-08 2:17
Daniel Siegl21-Nov-08 2:17 
GeneralJust what I was looking for!!! Pin
stinkman18-Sep-08 10:56
stinkman18-Sep-08 10:56 
GeneralNicer with extension methods Pin
lazygenius3-Sep-08 0:23
lazygenius3-Sep-08 0:23 
GeneralRe: Nicer with extension methods Pin
Arun Reginald Zaheeruddin24-Jul-09 22:22
Arun Reginald Zaheeruddin24-Jul-09 22:22 
QuestionI'am a beginner and don't know how to use graphics, need some help making the code brief Pin
Shimmy Weitzhandler18-Feb-08 11:45
Shimmy Weitzhandler18-Feb-08 11:45 
GeneralYour solution is perfect, but too complex Pin
kzerza18-Apr-07 23:28
kzerza18-Apr-07 23:28 
GeneralRe: Your solution is perfect, but too complex Pin
Michael90008-Jun-07 8:38
Michael90008-Jun-07 8:38 
GeneralRe: Your solution is perfect, but too complex Pin
Arun Reginald Zaheeruddin24-Jul-09 19:22
Arun Reginald Zaheeruddin24-Jul-09 19:22 
GeneralRe: Your solution is perfect, but too complex Pin
Michael900025-Aug-09 5:35
Michael900025-Aug-09 5:35 
GeneralFigure is not perferct Pin
e1e0n30-Jan-07 9:57
e1e0n30-Jan-07 9:57 
GeneralRe: Figure is not perferct Pin
shihyi12-Feb-07 21:18
shihyi12-Feb-07 21:18 
GeneralRe: Figure is not perferct Pin
Seishin#15-Jun-07 23:34
Seishin#15-Jun-07 23:34 
GeneralThanks for mentioning me! Pin
LuharVB25-Apr-06 6:48
LuharVB25-Apr-06 6:48 
GeneralRe: Thanks for mentioning me! Pin
Arun Reginald Zaheeruddin25-Apr-06 22:27
Arun Reginald Zaheeruddin25-Apr-06 22:27 
NewsBetter GetRoundedRect : Pin
Gooom30-Jan-06 5:27
Gooom30-Jan-06 5:27 
GeneralRe: Better GetRoundedRect : Pin
Timur Zanagar20-Jun-07 22:47
Timur Zanagar20-Jun-07 22:47 
GeneralRe: Better GetRoundedRect : Pin
Dana LeBeau18-Mar-08 15:27
Dana LeBeau18-Mar-08 15:27 
Thanks Gooom!!!! Fixed issue going on in my app. and thanks Arun for the article!
GeneralRe: Better GetRoundedRect : Pin
vjedlicka9-Nov-08 9:26
vjedlicka9-Nov-08 9:26 
GeneralRe: Better GetRoundedRect : better yet + bug fixes Pin
Brian Perrin18-Feb-09 17:21
Brian Perrin18-Feb-09 17:21 
GeneralRe: Better GetRoundedRect : better yet + bug fixes Pin
SonicMouse29-May-11 22:23
SonicMouse29-May-11 22:23 

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.