Table of Contents
The symbol returns the reader to the top of the Table of Contents.
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:
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:
public void DrawArc ( Pen pen,
int x,
int y,
int diameter,
int diameter,
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.
GraphicsPath rounded_rectangle_path ( int x,
int y,
int width,
int height )
{
int local_diameter = 0;
GraphicsPath path = new GraphicsPath ( );
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 ( );
path.AddArc ( ( x + width - local_diameter ),
( y + height - local_diameter ),
local_diameter,
local_diameter,
0.0F,
90.0F );
path.AddArc ( x,
( y + height - local_diameter ),
local_diameter,
local_diameter,
90.0F,
90.0F );
path.AddArc ( x,
y,
local_diameter,
local_diameter,
180.0F,
90.0F );
path.AddArc ( ( x + width - local_diameter ),
y,
local_diameter,
local_diameter,
270.0F,
90.0F );
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:
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
void draw_border_graphic ( )
{
GraphicsPath path = null;
path = rounded_rectangle_path ( 0,
0,
this.Width,
this.Height );
this.Region = new Region ( path );
border_graphic.Graphic.DrawPath (
new Pen ( BorderColor,
BorderThickness ),
path );
path.Dispose ( );
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.
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;
}
else
{
d = 255;
}
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.
References
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.