Title: Make a pie slice drawing extension in WPF and C#
This example shows how to make an extension method to draw a pie slice easily in WPF and C#. My post Make an intuitive extension method to draw an elliptical arc in WPF and C# shows how to make an extension method to draw elliptical arcs. The method described in this post adds line segments to an arc to make a pie slice.
The following XAML code shows the hierarchy of objects that you need to create to make an arc in WPF.
<Path Stroke="Blue" StrokeThickness="3" StrokeDashArray="5,5">
<Path.Data>
<PathGeometry>
<PathGeometry.Figures>
<PathFigureCollection>
<PathFigure StartPoint="62,114">
<PathFigure.Segments>
<PathSegmentCollection>
<ArcSegment Size="90,70"
RotationAngle="0"
IsLargeArc="True"
SweepDirection="Clockwise"
Point="198,159" />
</PathSegmentCollection>
</PathFigure.Segments>
</PathFigure>
</PathFigureCollection>
</PathGeometry.Figures>
</PathGeometry>
</Path.Data>
</Path>
The new extension method adds the two LineSegment objects to the PathSegmentCollection. A large part of the method is digging down through the object hierarchy to find the PathSegmentCollection where the lines belong.
The following code shows the new extension method.
// Draw an elliptical pie slice. Return the end points.
public static Path DrawPieSlice(this Canvas canvas,
Brush fill, Brush stroke, double stroke_thickness,
Rect rect, double angle1, double angle2,
bool is_large_arc, SweepDirection sweep_direction,
out Point point1, out Point point2)
{
// Draw the arc.
Path path = canvas.DrawArc(fill, stroke,
stroke_thickness, rect, angle1, angle2,
is_large_arc, sweep_direction,
out point1, out point2);
// Find the path's PathFigure collection.
PathGeometry path_geometry = (PathGeometry)path.Data;
PathFigureCollection path_figure_collection =
path_geometry.Figures;
PathFigure path_figure = path_figure_collection[0];
PathSegmentCollection path_segment_collection =
path_figure.Segments;
// Add the pie slice edges.
Point center = new Point(
(rect.Left + rect.Right) / 2,
(rect.Top + rect.Bottom) / 2);
LineSegment line_seg1 =
new LineSegment(center, true);
path_segment_collection.Add(line_seg1);
LineSegment line_seg2 =
new LineSegment(point1, true);
path_segment_collection.Add(line_seg2);
return path;
}
The method first calls the DrawArc extension method described in the earlier post. It then gets that object's Data property and converts it into a PathGeometry object. It then gets the PathFigureCollection stored in that object's Figures property. The DrawArc method only added one PathFigure to that collection, namely the PathFigure that contains the arc.
The code gets the collection's first entry, and then gets that entry's Segments property. This is the PathSegmentCollection object that holds the single ArcSegment created by the extension method.
Now the method is finally ready to create the new line segments. The PathFigure that contains the arc begins at the arc's start point, craws the arc, and then ends at the arc's end point. The new extension method creates a point representing the ellipse's center and makes a LineSegment from the arc's end point to the center point. It then adds the new LineSegment to the PathSegmentCollection after the arc.
The code then repeats the same steps to make the second LineSegment, this time starting at the center point and ending at the arc's starting point.
The method finishes by returning the Path object so the program can change its properties if it needs to do so.
The following code shows how the example program draws its pie chart.
private void Window_Loaded(object sender, RoutedEventArgs e)
{
Rect rect = new Rect(25, 70, 240, 140);
// Draw a pie slice.
Point point1, point2;
Path pie_slice1 = canDrawing.DrawPieSlice(
Brushes.Yellow, Brushes.Orange,
5, rect, 0, 0.15 * Math.PI, false,
SweepDirection.Clockwise,
out point1, out point2);
pie_slice1.StrokeLineJoin = PenLineJoin.Round;
// Draw a pie slice.
Path pie_slice2 = canDrawing.DrawPieSlice(
Brushes.Pink, Brushes.Red,
5, rect, 0.15 * Math.PI, 0.6 * Math.PI, false,
SweepDirection.Clockwise,
out point1, out point2);
pie_slice2.StrokeLineJoin = PenLineJoin.Round;
// Draw a pie slice.
Path pie_slice3 = canDrawing.DrawPieSlice(
Brushes.LightBlue, Brushes.Blue,
5, rect, 0.6 * Math.PI, 1.25 * Math.PI, false,
SweepDirection.Clockwise,
out point1, out point2);
pie_slice3.StrokeLineJoin = PenLineJoin.Round;
// Draw a pie slice.
Path pie_slice4 = canDrawing.DrawPieSlice(
Brushes.LightGreen, Brushes.Green,
5, rect, 1.25 * Math.PI, 1.6 * Math.PI, false,
SweepDirection.Clockwise,
out point1, out point2);
pie_slice4.StrokeLineJoin = PenLineJoin.Round;
// Draw a pie slice.
Path pie_slice5 = canDrawing.DrawPieSlice(
Brushes.Fuchsia, Brushes.Purple,
5, rect, 1.6 * Math.PI, 2 * Math.PI, false,
SweepDirection.Clockwise,
out point1, out point2);
pie_slice5.StrokeLineJoin = PenLineJoin.Round;
// Draw the ellipse.
//Ellipse ellipse = canDrawing.DrawEllipse(
// null, Brushes.Black, 2, rect);
//ellipse.StrokeDashArray =
// new DoubleCollection(new double[] {5, 5});
}
This code first defines a Rect object that defines the pie slices' ellipse. It uses the same Rect for every pie slice, so they all sit within the same ellipse.
This code then uses the same sequence of steps to draw each pie slice. It calls the DrawPieSlice extension method, passing it the slice's fill color, outline color, stroke thickness, and rectangle. The next parameters give the arc's start and stop angles measured counterclockwise in radians with 0 pointing to the right. (As is usual for most angular measurements in Windows.)
The next parameters indicate whether the arc is a large arc (it is not for these slices) and whether the arc should be drawn clockwise or counterclockwise. See the previous example for information about how those parameters work.
The final parameters let the extension method return the arc's end points.
After it creates a pie slice's Path object, the program sets the object's StrokeLineJoin property to Round to make the corners where the arc meets the line segments round. (The result in this example isn't bad if you omit this, but try commenting out all except the first pie slice skip this step.)
After it has drawn all of the slices, the code includes some commented out code to draw the ellipse surrounding the slices.
Download the example to experiment with it and to see additional details.
|