#1,152 – A Custom “Pie Slice” Shape
September 5, 2014 4 Comments
We can build on the earlier custom Arc shape code to create a custom shape that draws a slice of a pie. We only need to add a line segment back to the origin and set options to close the resulting curve and to fill in the middle.
Here is the resulting PieSlice class:
public class PieSlice : Shape { // Angle that arc starts at public double StartAngle { get { return (double)GetValue(StartAngleProperty); } set { SetValue(StartAngleProperty, value); } } // DependencyProperty - StartAngle private static PropertyMetadata startAngleMetadata = new PropertyMetadata( 0.0, // Default value null, // Property changed callback new CoerceValueCallback(CoerceAngle)); // Coerce value callback public static readonly DependencyProperty StartAngleProperty = DependencyProperty.Register("StartAngle", typeof(double), typeof(PieSlice), startAngleMetadata); // Angle that arc ends at public double EndAngle { get { return (double)GetValue(EndAngleProperty); } set { SetValue(EndAngleProperty, value); } } // DependencyProperty - EndAngle private static PropertyMetadata endAngleMetadata = new PropertyMetadata( 90.0, // Default value null, // Property changed callback new CoerceValueCallback(CoerceAngle)); // Coerce value callback public static readonly DependencyProperty EndAngleProperty = DependencyProperty.Register("EndAngle", typeof(double), typeof(PieSlice), endAngleMetadata); private static object CoerceAngle(DependencyObject depObj, object baseVal) { double angle = (double)baseVal; angle = Math.Min(angle, 359.9); angle = Math.Max(angle, 0.0); return angle; } protected override Geometry DefiningGeometry { get { double maxWidth = Math.Max(0.0, RenderSize.Width - StrokeThickness); double maxHeight = Math.Max(0.0, RenderSize.Height - StrokeThickness); //Console.WriteLine(string.Format("* maxWidth={0}, maxHeight={1}", maxWidth, maxHeight)); double xStart = maxWidth / 2.0 * Math.Cos(StartAngle * Math.PI / 180.0); double yStart = maxHeight / 2.0 * Math.Sin(StartAngle * Math.PI / 180.0); double xEnd = maxWidth / 2.0 * Math.Cos(EndAngle * Math.PI / 180.0); double yEnd = maxHeight / 2.0 * Math.Sin(EndAngle * Math.PI / 180.0); StreamGeometry geom = new StreamGeometry(); using (StreamGeometryContext ctx = geom.Open()) { ctx.BeginFigure( new Point((RenderSize.Width / 2.0) + xStart, (RenderSize.Height / 2.0) - yStart), true, // Filled true); // Closed ctx.ArcTo( new Point((RenderSize.Width / 2.0) + xEnd, (RenderSize.Height / 2.0) - yEnd), new Size(maxWidth / 2.0, maxHeight / 2), 0.0, // rotationAngle (EndAngle - StartAngle) > 180, // greater than 180 deg? SweepDirection.Counterclockwise, true, // isStroked false); ctx.LineTo(new Point((RenderSize.Width / 2.0), (RenderSize.Height / 2.0)), true, false); } return geom; } } }
To use the PieSlice from XAML, we specify a stroke color (for the outline), a fill color, and start and end angles.
<loc:PieSlice Stroke="Black" Fill="Black" Height="100" Width="100" Margin="5" StartAngle="0" EndAngle="60" HorizontalAlignment="Center"/>