|
I was going to write an article on doing this with extension methods but i'll add it here instead as i'm really just taking this code and modifying it slightly
public static void DrawRoundedRectangle(this Graphics g, Pen pen, Rectangle rect, int radius)
{
g.DrawRoundedRectangle(pen, rect.X, rect.Y, rect.Width, rect.Height, radius);
}
public static void DrawRoundedRectangle(this Graphics g, 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);
g.DrawRoundRectangle(pen, fx, fy, fwidth, fheight, fradius);
}
public static void DrawRoundRectangle(this Graphics g, Pen pen, float x, float y, float width, float height, float radius)
{
RectangleF rectangle = new RectangleF(x, y, width, height);
GraphicsPath path = g.GetRoundedRect(rectangle, radius);
g.DrawPath(pen, path);
}
private static GraphicsPath GetRoundedRect(this Graphics g, RectangleF baseRect, float radius)
{
if (radius <= 0.0F)
{
GraphicsPath mPath = new GraphicsPath();
mPath.AddRectangle(baseRect);
mPath.CloseFigure();
return mPath;
}
if (radius >= (Math.Min(baseRect.Width, baseRect.Height)) / 2.0)
return g.GetCapsule(baseRect);
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();
path.AddArc(arc, 180, 90);
arc.X = baseRect.Right - diameter;
path.AddArc(arc, 270, 90);
arc.Y = baseRect.Bottom - diameter;
path.AddArc(arc, 0, 90);
arc.X = baseRect.Left;
path.AddArc(arc, 90, 90);
path.CloseFigure();
return path;
}
public static GraphicsPath GetCapsule(this Graphics g, RectangleF baseRect)
{
float diameter;
RectangleF arc;
GraphicsPath path = new System.Drawing.Drawing2D.GraphicsPath();
try
{
if (baseRect.Width > baseRect.Height)
{
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)
{
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
{
path.AddEllipse(baseRect);
}
}
catch (Exception ex)
{
path.AddEllipse(baseRect);
}
finally
{
path.CloseFigure();
}
return path;
}
By using extension methods you don't need to derive a new type, and it will apply to all Graphics objects used in your project.
|
|
|
|
|
I debated about converting this code for Extension Methods but finally left this code as is for people who do not yet use newer version of the .Net Framework. However, I created a new article here[^] to demonstrate the power of Extension Methods with this code.
It works like a charm as you said and much of the hassle of creating and maintaining inherited objects is long gone. Ah, the relief!
Thanks for the comment even though it took me an year to reply to it.
Cheers,
Arun
The beginning of knowledge is the fear of God
|
|
|
|
|
I'm looking for a long time how I can make rounded corners rectangle, I'm happy, I finally found it.
my need in it is, I created a SplashScreen (my project is in vb, I'm using your class as dll) which it's BackgroundImage is set to some image.
I want the rectangle to be in the same size at the form, then fill the rectangle with the Form.Background image; set properties Form.BackColor and Form.TransparencyKey to the same color, and then, move all the labels to the foreground of the form, oops a wonderful splash screen.
if u use Visual Studio 2008, please observe it's splash screen, it has also a cool shadow which I don't know how to do, but let's forget this thing for right now.
Shimi)
Shimi
|
|
|
|
|
Hi!
Why you create new ExtendedGraphics class, just only for RoundRectangle drawing?
Stock Graphics has DrawPath and FillPath. It is ALL YOU NEED!
So, my example:
<br />
public class ExtendedPrimitives<br />
{<br />
<br />
public static GraphicsPath RoundRect(int x, int y,<br />
int width, int height,<br />
int radius, int lw)<br />
<br />
{<br />
GraphicsPath g = new GraphicsPath();<br />
int diameter = radius*2;<br />
g.AddArc(x+lw,y,diameter,diameter,180,90);<br />
g.AddArc(x+(width-diameter-lw),y,diameter,diameter,270,90);<br />
g.AddArc(x+(width-diameter-lw),y+(height-diameter-lw),<br />
diameter,diameter,360,90);<br />
g.AddArc(x+lw,y+(height-diameter-lw),diameter,diameter,90,90);<br />
g.CloseFigure();<br />
return g;<br />
<br />
}<br />
<br />
}
We have an ideal rounded rectangle
So, draw it:
<br />
GraphicsPath rrect = ExtendedPrimitives.RoundRect(100,100,100,130,3);<br />
g.DrawPath(new Pen(System.Drawing.Color.Navy,3),rrect);<br />
<br />
|
|
|
|
|
kzerza wrote: Stock Graphics has DrawPath and FillPath. It is ALL YOU NEED!
Nice example -
I just needed something basic like this
protected override void OnPaint(PaintEventArgs pe)
{
base.OnPaint(pe);
Rectangle baseRect = base.ClientRectangle;
float radius = 14;
System.Drawing.Drawing2D.GraphicsPath gp = new System.Drawing.Drawing2D.GraphicsPath();
gp.StartFigure();
gp.AddArc(baseRect.X, baseRect.Y, radius, radius, 180, 90);
gp.AddArc(baseRect.X + baseRect.Width - radius, baseRect.Y, radius, radius, 270, 90);
gp.AddArc(baseRect.X + baseRect.Width - radius, baseRect.Y + baseRect.Height - radius, radius, radius, 0, 90);
gp.AddArc(baseRect.X, baseRect.Y + baseRect.Height - radius, radius, radius, 90, 90);
gp.CloseFigure();
pe.Graphics.DrawPath(new Pen(Color.Blue), gp);
gp.Dispose();
}
|
|
|
|
|
My efforts have been largely focused towards keeping the code manageable and completely eliminating the need for a sub-class. Check out my updated instalment for the above code that requires you to extend or inherit nothing and just use the System.Drawing.Graphics class to do your work.
The updated instalment: Extended Graphics II [^]
Cheers,
Arun
The beginning of knowledge is the fear of God
|
|
|
|
|
|
Is it only me? Anyway, the rectangle isn't perfect. The only one top-left corner is good, otheres are not close enough to the rounded corners.
Any ideas?
Leo
|
|
|
|
|
I have the same question!!
How doesn't anybody reply this question?
James Jan
|
|
|
|
|
it's AddArc's fault..
after adding first arc you have to increase arc's diameter..
<br />
int corner = 20;<br />
GraphicsPath path = new GraphicsPath();<br />
Rectangle rectHelper = new Rectangle(0, 0, corner, corner);<br />
path.AddArc(rectHelper, 180, 90);<br />
<br />
corner++;<br />
rectHelper.Size = new Size(corner, corner);<br />
rectHelper.X = bounds.Width - corner-1;<br />
path.AddArc(rectHelper, 270, 90);<br />
<br />
rectHelper.Y = bounds.Height - corner-1;<br />
path.AddArc(rectHelper, 0, 90);<br />
<br />
rectHelper.X = 0;<br />
path.AddArc(rectHelper, 90, 90);<br />
<br />
path.CloseFigure();
life is study!!!
|
|
|
|
|
Hi,
I googled my name to find a piece of old code I had written awhile back and found this article. Very nice! Thanks for mentioning my name. It's nice to see somebody found my code useful and made something more out of it. Keep up the good work.
Tim Overbay
|
|
|
|
|
You're welcome Tim. For months, I had been trying to update and include new functionality into this class but don't have enough time to do so. Would you be more than willing to do so here? Thanks anyways for dropping by.
The beginning of knowledge is the fear of God
|
|
|
|
|
because that bug for exemple at radius 1.7F ...
<br />
private GraphicsPath GetRoundedRect(RectangleF baseRect, float radius) <br />
{<br />
if( radius<=0.0F ) <br />
{ <br />
GraphicsPath mPath = new GraphicsPath(); <br />
mPath.AddRectangle(baseRect); <br />
mPath.CloseFigure(); <br />
return mPath;<br />
}<br />
<br />
if( radius>=(Math.Min(baseRect.Width, baseRect.Height))/2.0) <br />
return GetCapsule( baseRect ); <br />
<br />
<br />
GraphicsPath gp = new GraphicsPath();<br />
gp.StartFigure();<br />
gp.AddArc(baseRect.X, baseRect.Y, radius, radius, 180, 90);<br />
gp.AddArc(baseRect.X + baseRect.Width - radius, baseRect.Y, radius, radius, 270, 90);<br />
gp.AddArc(baseRect.X + baseRect.Width - radius, baseRect.Y + baseRect.Height - radius, radius, radius, 0, 90);<br />
gp.AddArc(baseRect.X, baseRect.Y + baseRect.Height - radius, radius, radius, 90, 90);<br />
gp.CloseFigure();<br />
return gp;<br />
} <br />
|
|
|
|
|
Hi,
I just wanted to test your code but what is
return GetCapsule( baseRect );
Is there something missing?
|
|
|
|
|
Thanks Gooom!!!! Fixed issue going on in my app. and thanks Arun for the article!
|
|
|
|
|
public static GraphicsPath GetRoundedRect(RectangleF baseRect, float radiusX, float radiusY)
{
if (radiusX <= 0.0F)
{
GraphicsPath mPath = new GraphicsPath();
mPath.AddRectangle(baseRect);
mPath.CloseFigure();
return mPath;
}
if (radiusX >= (Math.Min(baseRect.Width, baseRect.Height)) / 2.0)
return GetCapsule(baseRect);
GraphicsPath gp = new GraphicsPath();
gp.StartFigure();
gp.AddArc(baseRect.X, baseRect.Y, radiusX, radiusY, 180, 90);
gp.AddArc(baseRect.X + baseRect.Width - radiusX, baseRect.Y, radiusX, radiusY, 270, 90);
gp.AddArc(baseRect.X + baseRect.Width - radiusX, baseRect.Y + baseRect.Height - radiusY, radiusX, radiusY, 0, 90);
gp.AddArc(baseRect.X, baseRect.Y + baseRect.Height - radiusY, radiusX, radiusY, 90, 90);
gp.CloseFigure();
return gp;
}
|
|
|
|
|
Fixed:
* AddArc needs diameter, not radius
* missing GetCapsule (turns out we don't need it)
* X and Y components work properly
public static GraphicsPath GetRoundedRect(RectangleF r, float radiusX, float radiusY)
{
GraphicsPath gp = new GraphicsPath();
gp.StartFigure();
if (radiusX <= 0.0F || radiusY <= 0.0F) {
gp.AddRectangle(r);
} else {
PointF d = new PointF(Math.Min(radiusX * 2, r.Width)
, Math.Min(radiusY * 2, r.Height));
gp.AddArc(r.X, r.Y, d.X, d.Y, 180, 90);
gp.AddArc(r.Right - d.X, r.Y, d.X, d.Y, 270, 90);
gp.AddArc(r.Right - d.X, r.Bottom - d.Y, d.X, d.Y, 0, 90);
gp.AddArc(r.X, r.Bottom - d.Y, d.X, d.Y, 90, 90);
}
gp.CloseFigure();
return gp;
}
|
|
|
|
|
Best example yet... thanks..
Here it is for you old school C++ folks:
static __inline void GenerateRoundedRectangle(GraphicsPath& gp, const RectF& rc, float flRadX, float flRadY){
gp.StartFigure();
if(flRadX <= 0.0f || flRadY <= 0.0f){
gp.AddRectangle(rc);
}else{
const PointF d(__min(flRadX * 2.0f, rc.Width), __min(flRadY * 2.0f, rc.Height));
gp.AddArc(rc.X, rc.Y, d.X, d.Y, 180.0f, 90.0f);
gp.AddArc(rc.GetRight() - d.X, rc.Y, d.X, d.Y, 270.0f, 90.0f);
gp.AddArc(rc.GetRight() - d.X, rc.GetBottom() - d.Y, d.X, d.Y, 0.0f, 90.0f);
gp.AddArc(rc.X, rc.GetBottom() - d.Y, d.X, d.Y, 90.0f, 90.0f);
}
gp.CloseFigure();
}
|
|
|
|
|
Simple, functional, and easy to use.
Two thumbs up
the only additions I would make is
#region "Added Rectangles"
public void DrawRoundRectangle(System.Drawing.Pen pen, Rectangle rec, int radius)
{
this.DrawRoundRectangle(pen, rec.X, rec.Y, rec.Width, rec.Height, radius);
}
public void FillRoundRectangle(System.Drawing.Brush brush, Rectangle rec, int radius)
{
this.FillRoundRectangle(brush, rec.X, rec.Y, rec.Width, rec.Height, radius);
}
#endregion
Just added the ability to use rects - saves some typing
|
|
|
|
|
I would be adding some extra functionality to the class and would require ideas and requirements from the readers. I'll also try to include some examples and screenshots alongwith the code in the article. Please wait a while, for there's more to come.
The beginning of knowledge is the fear of God
|
|
|
|
|
Support for X and Y radii. I think this will make it complete.
Best regards,
Paul.
Jesus Christ is LOVE! Please tell somebody.
|
|
|
|
|
Thank a lot at this:
using System;
using System.Drawing;
using System.Drawing.Drawing2D;
namespace RoundRect
{
public sealed class ExtendedGraphics
{
#region Constructors and Destructor
private ExtendedGraphics()
{
}
#endregion
#region Public Methods
public static void FillRoundRectangle(Graphics g, Brush brush, int x, int y,
int width, int height, int radiusX, int radiusY)
{
FillRoundRectangle(g, brush, (float)x, (float)y,
(float)width, (float)height, (float)radiusX, (float)radiusY);
}
public static void FillRoundRectangle(Graphics g, Brush brush, float x, float y,
float width, float height, float radiusX, float radiusY)
{
if (g != null)
{
RectangleF rectangle = new RectangleF(x, y, width, height);
GraphicsPath path = GetRoundedRect(rectangle, radiusX, radiusY);
g.FillPath(brush, path);
}
}
public static void DrawRoundRectangle(Graphics g, Pen pen, int x, int y,
int width, int height, int radiusX, int radiusY)
{
DrawRoundRectangle(g, pen, (float)x, (float)y, (float)width,
(float)height, (float)radiusX, (float)radiusY);
}
public static void DrawRoundRectangle(Graphics g, Pen pen, float x, float y,
float width, float height, float radiusX, float radiusY)
{
if (g != null)
{
RectangleF rectangle = new RectangleF(x, y, width, height);
GraphicsPath path = GetRoundedRect(rectangle, radiusX, radiusY);
g.DrawPath(pen, path);
}
}
#endregion
#region Private Methods
private static GraphicsPath GetRoundedRect(RectangleF baseRect,
float radiusX, float radiusY)
{
if (radiusX <= 0.0F || radiusY <= 0.0F)
{
GraphicsPath mPath = new GraphicsPath();
mPath.AddRectangle(baseRect);
mPath.CloseFigure();
return mPath;
}
if (radiusX >= (Math.Min(baseRect.Width, baseRect.Height)) / 2.0)
return GetCapsule(baseRect);
if (radiusY >= (Math.Min(baseRect.Width, baseRect.Height)) / 2.0)
return GetCapsule(baseRect);
float diameterX = radiusX * 2.0F;
float diameterY = radiusY * 2.0F;
SizeF sizeF = new SizeF(diameterX, diameterY);
RectangleF arc = new RectangleF(baseRect.Location, sizeF);
GraphicsPath path = new GraphicsPath();
path.AddArc(arc, 180, 90);
arc.X = baseRect.Right - diameterX;
path.AddArc(arc, 270, 90);
arc.Y = baseRect.Bottom - diameterY;
path.AddArc(arc, 0, 90);
arc.X = baseRect.Left;
path.AddArc(arc, 90, 90);
path.CloseFigure();
return path;
}
private static GraphicsPath GetCapsule(RectangleF baseRect)
{
GraphicsPath path = new GraphicsPath();
try
{
if (baseRect.Width > baseRect.Height)
{
float diameter = baseRect.Height;
SizeF sizeF = new SizeF(diameter, diameter);
RectangleF 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)
{
float diameter = baseRect.Width;
SizeF sizeF = new SizeF(diameter, diameter);
RectangleF arc = new RectangleF(baseRect.Location, sizeF);
path.AddArc(arc, 180, 180);
arc.Y = baseRect.Bottom - diameter;
path.AddArc(arc, 0, 180);
}
else
{
path.AddEllipse(baseRect);
}
}
catch
{
path.AddEllipse(baseRect);
}
finally
{
path.CloseFigure();
}
return path;
}
#endregion
}
}
Best regards,
Paul.
Jesus Christ is LOVE! Please tell somebody.
|
|
|
|
|
With this sample:
using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;
namespace RoundRect
{
public class MainForm : System.Windows.Forms.Form
{
private ExtendedGraphics m_objGraphics;
private System.ComponentModel.Container components = null;
public MainForm()
{
InitializeComponent();
m_objGraphics = new ExtendedGraphics();
}
protected override void Dispose( bool disposing )
{
if( disposing )
{
if (components != null)
{
components.Dispose();
}
}
base.Dispose( disposing );
}
#region Windows Form Designer generated code
private void InitializeComponent()
{
this.AutoScaleBaseSize = new System.Drawing.Size(5, 12);
this.BackColor = System.Drawing.SystemColors.Window;
this.ClientSize = new System.Drawing.Size(568, 494);
this.Name = "MainForm";
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
this.Text = "RoundRect Test";
this.Resize += new System.EventHandler(this.OnResize);
this.MouseDown += new System.Windows.Forms.MouseEventHandler(this.OnMouseDown);
this.Load += new System.EventHandler(this.OnLoad);
this.Paint += new System.Windows.Forms.PaintEventHandler(this.OnPaint);
}
#endregion
[STAThread]
static void Main()
{
Application.Run(new MainForm());
}
private void OnLoad(object sender, System.EventArgs e)
{
}
private void OnPaint(object sender, System.Windows.Forms.PaintEventArgs e)
{
m_objGraphics.Graphics = e.Graphics;
Rectangle rectClient = this.ClientRectangle;
rectClient.Inflate(-rectClient.Width/8, -rectClient.Height/8);
Color penColor = Color.FromArgb(64, Color.Blue);
Pen pen = new Pen(penColor, 4f);
int radiusX = rectClient.Width/4;
int radiusY = rectClient.Height/4;
e.Graphics.DrawRectangle(Pens.Green, rectClient);
m_objGraphics.DrawRoundRectangle(pen, rectClient.X, rectClient.Y,
rectClient.Width, rectClient.Height, radiusX, radiusY);
pen.Dispose();
Rectangle rcTL = new Rectangle(rectClient.Left, rectClient.Top,
radiusX * 2, radiusY * 2);
e.Graphics.DrawEllipse(Pens.Red, rcTL);
Rectangle rcBL = new Rectangle(rectClient.Left,
rectClient.Bottom - radiusY * 2,
radiusX * 2, radiusY * 2);
e.Graphics.DrawEllipse(Pens.Red, rcBL);
Rectangle rcTR = new Rectangle(rectClient.Right - radiusX * 2,
rectClient.Top,
radiusX * 2, radiusY * 2);
e.Graphics.DrawEllipse(Pens.Red, rcTR);
Rectangle rcBR = new Rectangle(rectClient.Right - radiusX * 2,
rectClient.Bottom - radiusY * 2,
radiusX * 2, radiusY * 2);
e.Graphics.DrawEllipse(Pens.Red, rcBR);
m_objGraphics.Graphics = null;
}
private void OnMouseDown(object sender, System.Windows.Forms.MouseEventArgs e)
{
}
private void OnResize(object sender, System.EventArgs e)
{
Invalidate();
}
}
}
When you try resizing the form, sometimes the corner ellipses are not aligned correctly with round rectangle. Is there any possible improvement?
Best regards,
Paul.
Jesus Christ is LOVE! Please tell somebody.
|
|
|
|
|
Just an annoyance, but I hate compiler warnings, if you dont plan on using the exception details don't specify them. Nice code though.
Change the following:
catch (Exception ex)
{
path.AddEllipse(baseRect);
}
to:
catch (Exception)
{
path.AddEllipse(baseRect);
}
|
|
|
|
|
Thanks for the comment. I'll fix the code once I'm finished with the Earthquake disaster relief-work here in Pakistan. I do, nevertheless, appreciate your feedback. I know I have to clean up the whole code and more yet the article itself.
The beginning of knowledge is the fear of God
|
|
|
|
|