namespace Utilities { using System; using System.Windows; using System.Windows.Media; using System.Windows.Shapes; public class Arc: Shape { public static readonly DependencyProperty EndAngleProperty = DependencyProperty.Register("EndAngle", typeof(double), typeof(Arc), new FrameworkPropertyMetadata((double)315, FrameworkPropertyMetadataOptions.AffectsRender | FrameworkPropertyMetadataOptions.AffectsMeasure)); public static readonly DependencyProperty StartAngleProperty = DependencyProperty.Register("StartAngle", typeof(double), typeof(Arc), new FrameworkPropertyMetadata((double)0, FrameworkPropertyMetadataOptions.AffectsRender | FrameworkPropertyMetadataOptions.AffectsMeasure)); public static readonly DependencyProperty IsStrokeClosedProperty = DependencyProperty.Register("IsStrokeClosed", typeof(bool), typeof(Arc), new FrameworkPropertyMetadata(true, FrameworkPropertyMetadataOptions.AffectsRender | FrameworkPropertyMetadataOptions.AffectsMeasure)); static Arc() { Shape.StretchProperty.OverrideMetadata(typeof(Arc), new FrameworkPropertyMetadata(Stretch.Uniform)); // Make *something* appear when first adding it. Shape.FillProperty.OverrideMetadata(typeof(Arc), new FrameworkPropertyMetadata(Brushes.Gray)); } public double EndAngle { get { return (double)this.GetValue(Arc.EndAngleProperty); } set { this.SetValue(Arc.EndAngleProperty, value); } } public double StartAngle { get { return (double)this.GetValue(Arc.StartAngleProperty); } set { this.SetValue(Arc.StartAngleProperty, value); } } public bool IsStrokeClosed { get { return (bool)this.GetValue(Arc.IsStrokeClosedProperty); } set { this.SetValue(Arc.IsStrokeClosedProperty, value); } } protected override Geometry DefiningGeometry { get { PathGeometry geometry = new PathGeometry(); PathFigure figure = new PathFigure(); figure.StartPoint = new Point(1, 1); double startAngle = this.StartAngle; double endAngle = this.EndAngle; while (endAngle < startAngle) endAngle += 360; double startTheta = (startAngle - 90) / 180 * Math.PI; double startX = Math.Cos(startTheta) + figure.StartPoint.X; double startY = Math.Sin(startTheta) + figure.StartPoint.Y; bool isStrokeClosed = this.IsStrokeClosed; figure.Segments.Add(new LineSegment(new Point(startX, startY), isStrokeClosed)); double theta = (endAngle - 90) / 180 * Math.PI; double finalX = Math.Cos(theta) + figure.StartPoint.X; double finalY = Math.Sin(theta) + figure.StartPoint.Y; bool isLargeAngle = (endAngle - startAngle) > 180; figure.Segments.Add(new ArcSegment(new Point(finalX, finalY), new Size(1, 1), endAngle, isLargeAngle, SweepDirection.Clockwise, true)); figure.Segments.Add(new LineSegment(new Point(1, 1), isStrokeClosed)); geometry.Figures.Add(figure); // Shape by default stretches the geometry to fill the area, which is all good, // but we want the area to be the full circle, even when only a wedge is shown. PathFigure crossbar = new PathFigure(); crossbar.StartPoint = new Point(0, 0); crossbar.Segments.Add(new LineSegment(new Point(2, 2), false)); geometry.Figures.Add(crossbar); return geometry; } } } }