#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"/>
Pingback: Dew Drop – September 5, 2014 (#1849) | Morning Dew
Hi Sean,
Very good explained example.
Is there any option to add Click on PieSlice?
To let binding work, you should define Start and End angle metadata as FrameworkPropertyMetadata with FrameworkPropertyMetadataOptions.AffectsRender flag set. Otherwise, setting the value at runtime will not affect the control.
Have you ever tried to change Size in designer?
Makes it bigger, works fine. But resize it smaller, won’t work.
Could you explain why?