15,352,679 members
Articles / Multimedia / GDI+
Article
Posted 18 Jul 2015

27.8K views
20 bookmarked

# RoundedButton Control - Demystifying DrawArc

Rate me:
18 Jul 2015CPOL6 min read
This article presents the RoundedButton control and describes the .Net DrawArc method, the subject of a number of questions on programming forums.

## Introduction

This article presents the RoundedButton control. It was developed during a revision to the Known Colors Palette Tool. This article not only describes the workings of the RoundedButton control, but it also discusses the .Net Graphics.DrawArc method, the subject of a number of questions on programming forums.

All of the graphics, presented in this article, initially display as thumbnails. This allows the reader to click on an image to open it in the reader's default graphics display program. All images in this article are PNG images.

In the following discussions, properties, specified by the developer, are displayed in BoldMixedCase text; variables, used internally by the software, are displayed in italicized_lowercase text.

## Background

In .Net, the ellipse is the basis for drawing ellipses, circles, and arcs. However, as would be the case, Microsoft has made some modifications to the graphics surfaces. In the figure to the left, ( 0, 0 ) is the top-left corner of a graphics surface (e.g., windows form or control origin).

In the normal world (i.e., non-.Net), x-coordinates have increasing values to the right; y-coordinates have increasing values upward. In .Net, x-coordinates have increasing values to the right; but y-coordinates have increasing values downward.

Also, in the normal world, angles are measured counter-clockwise from the x-axis and usually are expressed in radians. In .Net, angles are measured clockwise from the x-axis and are expressed in either radians or degrees, depending upon the method invoked.

Recall the ellipse from Analytical Geometry. Its definition is:

A curved line forming a closed loop, where the sum of the distances from two points (foci) to every point on the line is constant.

A circle is a special case of an ellipse wherein the major axis and the minor axis are equal. When this holds true, the two foci are collocated at the center and we call the axes the circle's diameter.

In .Net, ellipses are defined in terms of the figure's top-left corner and its width and height.

The figure to the left depicts the.Net view. To draw an ellipse, one invokes the Graphics.DrawEllipse method:

C#
```public void DrawEllipse ( Pen pen,
int x,
int y,
int width,
int height )
```

The color and thickness of the elliptical figure are set by the color and width properties of the Pen supplied in the invocation of DrawEllipse. If it is desired to draw a circle, rather than an ellipse, the width and height are set equal.

Drawing an arc is almost exactly the same as drawing an ellipse, only with two additional parameters: the start angle and the sweep angle.

In the figure to the left, a circular arc has been drawn starting at the angle 180° and extending through a sweep angle of 90° clockwise. To accomplish this we invoke the Graphics.DrawArc method:

C#
```public void DrawArc ( Pen pen,
int x,
int y,
int diameter,   // width
int diameter,   // height
int start_angle,
int sweep_angle )
```

Because we desire a circular arc, width and height are set equal (to the diameter of the circle).

Note the similarities between DrawArc and DrawEllipse. If the start_angle and sweep_angle were removed from the parameters of the DrawArc method, we would be left with the parameters for the DrawEllipse method. So DrawArc can be viewed as drawing the same figure as would be drawn by DrawEllipse but with only that portion of the line starting at start_angle and extending for sweep_angle visible.

## RoundedButton Control

### Construction

Consider the figure to the left. For illustrative purposes, the RoundedButton control outline is drawn in black; the four arcs are drawn in red; the four green lines are the result of invoking the Graphics.CloseAllFigures method; the orange lines are the height for each arc; the blue lines are the width for each arc; and the black dots are the origins for each arc. Note that the orange and blue lines are of equal length, forming the diameters of the circles on which the arcs are drawn.

To draw a figure made up of the red and green lines, we use a GraphicsPath . The GraphicsPath is created by the rounded_rectangle_path method.

C#
```// ************************************ rounded_rectangle_path

/// <summary>
/// computes the GraphicsPath of a rounded rectangle
/// </summary>
/// <param name="x">
/// x coordinate of the upper left corner of the rectangle
/// </param>
/// <param name="y">
/// y coordinate of the upper left corner of the rectangle
/// </param>
/// <param name="width">
/// width of the rectangle
/// </param>
/// <param name="height">
/// height of the rectangle
/// </param>
/// radius of the circle that defines the rounded corner
/// </param>
/// <returns>
/// the GraphicsPath that defines the rounded rectangle
/// </returns>
GraphicsPath rounded_rectangle_path ( int x,
int y,
int width,
int height )
{
int           local_diameter = 0;
GraphicsPath  path = new GraphicsPath ( );
// take into account right and
// bottom sides
x += 1;
y += 1;
width -= 1;
height -= 1;

if ( Diameter == 0 )
{
local_diameter = Math.Min ( width, height );
local_diameter =
round ( ( float ) local_diameter / 2.0F );
}
else
{
local_diameter = Diameter;
}

path.StartFigure ( );
// bottom right
path.AddArc ( ( x + width - local_diameter ),
( y + height - local_diameter ),
local_diameter,
local_diameter,
0.0F,
90.0F );
// bottom left
( y + height - local_diameter ),
local_diameter,
local_diameter,
90.0F,
90.0F );
// top left
y,
local_diameter,
local_diameter,
180.0F,
90.0F );
// top right
path.AddArc ( ( x + width - local_diameter ),
y,
local_diameter,
local_diameter,
270.0F,
90.0F );
// join all arcs together
path.CloseAllFigures ( );

return ( path );
}
```

The value of local_diameter is derived from the Diameter property. During the development of this control, various values of the diameter were tried. The "best" one turned out to be based on the formula:

C#
```Min ( width, height ) / 2.0F )
```

If the supplied value of the Diameter property is zero, the control will calculate the value of local_diameter from this formula.

rounded_rectangle_path returns a GraphicsPath. From MSDN documentation, a GraphicsPath can be used to

draw outlines of shapes, fill the interiors of shapes, and create clipping regions.

The GraphicsPath returned by rounded_rectangle_path will be used to create a clipping region that limits all drawing to the desired areas of the control and to draw the outline of the RoundedButton control. rounded_rectangle_path is invoked by draw_border_graphic

C#
```// *************************************** draw_border_graphic

/// <summary>
/// creates the border_graphic GraphicsBuffer by performing the
/// following actions:
///
///     1.  create a GraphicsPath
///     2.  establish the clipping region that limits graphics
///         operations to within the GraphicsPath
///     3.  draws the outline of the GraphicsPath to the
///         border_graphic GraphicsBuffer
///     4.  deletes the GraphicsPath
///     5.  optionally draws the outlines of the circles that
///         were used to create the rounded corners to the
///         border_graphic GraphicsBuffer
/// </summary>
void draw_border_graphic ( )
{
GraphicsPath    path = null;
// ORDER IS IMPORTANT!!!

// compute the path
path = rounded_rectangle_path ( 0,
0,
this.Width,
this.Height );
// set clipping region
this.Region = new Region ( path );
// draw the border
border_graphic.Graphic.DrawPath (
new Pen ( BorderColor,
BorderThickness ),
path );
path.Dispose ( );
// draw circles
if ( DrawRoundingCircles )
{
draw_rounding_circles ( border_graphic.Graphic,
0,
0,
this.Width,
this.Height );
}
}
```

The GraphicsBuffer is a class that provides a flicker-free drawing surface. The draw_rounding_circles method is very much like the rounded_rectangle_path method with the exception that Graphics.DrawEllipse replaces GraphicsPath.AddArc .

### Properties

The following are the properties unique to the RoundedButton control. The properties of the Button class, from which RoundedButton inherits, are not discussed here.

 Property Type Default Purpose BorderColor Color Color.White Sets/Gets color of the button border BorderThickness int 1 Sets/Gets thickness of button border Diameter int 0 Sets/Gets diameter for rounding corners. If the supplied value of Diameter is zero, the control will calculate the value of the rounding corner diameters as Min ( width, height ) / 2.0F ). DrawRoundingCircles bool false Specifies if rounding corner circles are to be drawn

## Rounded Button Demonstration

The download contains the Rounded Button Dialog demonstration application that allows its users to determine what the RoundedButton control will look like under various circumstances.

The Button GroupBox allows the user to modify the size and color of the RoundedButton. The Width and Height NumericUpDown controls are both limited to the range [ 22, 272 ]. Both affect the RoundedButton Width and Height properties. The BackColor and ForeColor Buttons allow the user to set the associated RoundedButton properties. Note that the ForeColor is used when drawing any text on the face of the RoundedButton. The Use a contrasting ForeColor CheckBox determines if RoundedButton text is displayed using the user-specified ForeColor or a contrasting Black or White color that depends upon the RoundedButton BackColor.

The Border GroupBox allows the user to set the RoundedButton border properties. The Diameter NumericUpDown control specifies the diameter of the rounding corner circles. The lower limit of Diameter is zero; the upper limit is Min ( width, height ) / 2. As the height and width change, so too does the upper limit of Diameter. The Thickness NumericUpDown control specifies the width of the border. It is limited in range to [ 0, 100 ]. The Color Button sets the color of the border. The Draw Rounding Circles CheckBox is present for illustration purposes only. It should remain unchecked in the production environment.

For readers interested in how the contrasting colors were determined, the following code was used.

C#
```// ***************************************** contrasting_color

/// <summary>
/// determines the color (black or white) that contrasts with
/// the given color
/// </summary>
/// <param name="color">
/// color for which to find its contrasting color
/// </param>
/// <returns>
/// the contrasting color (black or white)
/// </returns>
/// <reference>
/// http://stackoverflow.com/questions/1855884/
///     determine-font-color-based-on-background-color
/// </reference>
Color contrasting_color ( Color color )
{
double  a;
int     d = 0;

a = 1.0 - ( ( 0.299 * color.R +
0.587 * color.G +
0.114 * color.B ) / 255.0 );

if ( a < 0.5 )
{
d = 0;                  // bright colors - black font
}
else
{
d = 255;                // dark colors - white font
}

return ( Color.FromArgb ( d, d, d ) );
}
```

## Conclusion

This article has presented the RoundedButton control and described in some detail the workings of the .Net DrawArc method.

## Development Environment

The RoundedButton control was developed in the following environment:

 Microsoft Windows 7 Professional Service Pack 1 Microsoft Visual Studio 2008 Professional Microsoft .Net Framework Version 3.5 SP1 Microsoft Visual C# 2008

## History

• July 18, 2015 - Original article.

## About the Author

 Software Developer (Senior) United States
In 1964, I was in the US Coast Guard when I wrote my first program. It was written in RPG (note no suffixing numbers). Programs and data were entered using punched cards. Turnaround was about 3 hours. So much for the "good old days!"

In 1970, when assigned to Washington DC, I started my MS in Mechanical Engineering. I specialized in Transportation. Untold hours in statistical theory and practice were required, forcing me to use the university computer and learn the FORTRAN language, still using punched cards!

In 1973, I was employed by the Norfolk VA Police Department as a crime analyst for the High Intensity Target program. There, I was still using punched cards!

In 1973, I joined Computer Sciences Corporation (CSC). There, for the first time, I was introduced to a terminal with the ability to edit, compile, link, and test my programs on-line. CSC also gave me the opportunity to discuss technical issues with some of the brightest minds I've encountered during my career.

In 1975, I moved to San Diego to head up an IR&D project, BIODAB. I returned to school (UCSD) and took up Software Engineering at the graduate level. After BIODAB, I headed up a team that fixed a stalled project. I then headed up one of the two most satisfying projects of my career, the Automated Flight Operations Center at Ft. Irwin, CA.

I left Anteon Corporation (the successor to CSC on a major contract) and moved to Pensacola, FL. For a small company I built their firewall, given free to the company's customers. An opportunity to build an air traffic controller trainer arose. This was the other most satisfying project of my career.

Today, I consider myself capable.

## Comments and Discussions

 First Prev Next
 Nice, but ... RickZeeland18-Jul-15 21:53 RickZeeland 18-Jul-15 21:53
 Re: Nice, but ... gggustafson19-Jul-15 4:47 gggustafson 19-Jul-15 4:47
 Re: Nice, but ... RickZeeland19-Jul-15 6:59 RickZeeland 19-Jul-15 6:59
 Re: Nice, but ... gggustafson19-Jul-15 7:01 gggustafson 19-Jul-15 7:01
 Re: Nice, but ... RickZeeland19-Jul-15 7:10 RickZeeland 19-Jul-15 7:10
 Re: Nice, but ... gggustafson19-Jul-15 9:00 gggustafson 19-Jul-15 9:00
 Re: Nice, but ... RickZeeland20-Jul-15 7:40 RickZeeland 20-Jul-15 7:40
 Re: Nice, but ... gggustafson20-Jul-15 7:49 gggustafson 20-Jul-15 7:49
 Nice Eric Lapouge18-Jul-15 18:35 Eric Lapouge 18-Jul-15 18:35
 Nice as always. Not sure I'll do something with it, but well documented like the previous.
 Re: Nice gggustafson19-Jul-15 4:47 gggustafson 19-Jul-15 4:47
 Last Visit: 31-Dec-99 18:00     Last Update: 30-Jun-22 19:49 Refresh 1

General    News    Suggestion    Question    Bug    Answer    Joke    Praise    Rant    Admin

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