Comment dessiner un arc avec un rayon et un angle de départ et d’arrêt

Si j’ai les quatre propriétés suivantes dans mon DataContext de mon élément Canvas

Point Center double Radius double StartAngle double EndAngle 

puis-je dessiner un arc sans code supplémentaire derrière?

Fournir un composant personnalisé s’est avéré être la meilleure solution. Je l’utilise comme ça dans mon code

  

SmallAngle quand true rendra le petit angle entre les points indépendamment de l’ordre de StartAngle et EndAngle . Lorsque SmallAngle est SmallAngle false l’arc est rendu dans le sens inverse des aiguilles d’une montre.

La mise en œuvre est

 using System; using System.Collections.Generic; using System.Windows; using System.Windows.Media; using System.Windows.Shapes; public sealed class Arc : Shape { public Point Center { get => (Point)GetValue(CenterProperty); set => SetValue(CenterProperty, value); } // Using a DependencyProperty as the backing store for Center. This enables animation, styling, binding, etc... public static readonly DependencyProperty CenterProperty = DependencyProperty.Register(nameof(Center), typeof(Point), typeof(Arc), new FrameworkPropertyMetadata(new Point(), FrameworkPropertyMetadataOptions.AffectsRender)); public double StartAngle { get => (double)GetValue(StartAngleProperty); set => SetValue(StartAngleProperty, value); } // Using a DependencyProperty as the backing store for StartAngle. This enables animation, styling, binding, etc... public static readonly DependencyProperty StartAngleProperty = DependencyProperty.Register(nameof(StartAngle), typeof(double), typeof(Arc), new FrameworkPropertyMetadata(0.0, FrameworkPropertyMetadataOptions.AffectsRender)); public double EndAngle { get => (double)GetValue(EndAngleProperty); set => SetValue(EndAngleProperty, value); } // Using a DependencyProperty as the backing store for EndAngle. This enables animation, styling, binding, etc... public static readonly DependencyProperty EndAngleProperty = DependencyProperty.Register(nameof(EndAngle), typeof(double), typeof(Arc), new FrameworkPropertyMetadata(Math.PI / 2.0, FrameworkPropertyMetadataOptions.AffectsRender)); public double Radius { get => (double)GetValue(RadiusProperty); set => SetValue(RadiusProperty, value); } // Using a DependencyProperty as the backing store for Radius. This enables animation, styling, binding, etc... public static readonly DependencyProperty RadiusProperty = DependencyProperty.Register(nameof(Radius), typeof(double), typeof(Arc), new FrameworkPropertyMetadata(10.0, FrameworkPropertyMetadataOptions.AffectsRender)); public bool SmallAngle { get => (bool)GetValue(SmallAngleProperty); set => SetValue(SmallAngleProperty, value); } // Using a DependencyProperty as the backing store for SmallAngle. This enables animation, styling, binding, etc... public static readonly DependencyProperty SmallAngleProperty = DependencyProperty.Register(nameof(SmallAngle), typeof(bool), typeof(Arc), new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.AffectsRender)); static Arc() => DefaultStyleKeyProperty.OverrideMetadata(typeof(Arc), new FrameworkPropertyMetadata(typeof(Arc))); protected override Geometry DefiningGeometry { get { double a0 = StartAngle < 0 ? StartAngle + 2 * Math.PI : StartAngle; double a1 = EndAngle < 0 ? EndAngle + 2 * Math.PI : EndAngle; if (a1 < a0) a1 += Math.PI * 2; SweepDirection d = SweepDirection.Counterclockwise; bool large; if (SmallAngle) { large = false; d = (a1 - a0) > Math.PI ? SweepDirection.Counterclockwise : SweepDirection.Clockwise; } else large = (Math.Abs(a1 - a0) < Math.PI); Point p0 = Center + new Vector(Math.Cos(a0), Math.Sin(a0)) * Radius; Point p1 = Center + new Vector(Math.Cos(a1), Math.Sin(a1)) * Radius; List segments = new List { new ArcSegment(p1, new Size(Radius, Radius), 0.0, large, d, true) }; List figures = new List { new PathFigure(p0, segments, true) { IsClosed = false } }; return new PathGeometry(figures, FillRule.EvenOdd, null); } } } 

Puis-je proposer une solution légèrement différente?

 class ArcII:FrameworkElement { ///  /// Center point of Arc. ///  [Category("Arc")] public Point Center { get { return (Point)GetValue(CenterProperty); } set { SetValue(CenterProperty, value); } } // Using a DependencyProperty as the backing store for Center. This enables animation, styling, binding, etc... public static readonly DependencyProperty CenterProperty = DependencyProperty.Register("Center", typeof(Point), typeof(ArcII), new FrameworkPropertyMetadata(new Point(0, 0), FrameworkPropertyMetadataOptions.AffectsRender)); ///  /// Forces the Arc to the center of the Parent container. ///  [Category("Arc")] public bool OverrideCenter { get { return (bool)GetValue(OverrideCenterProperty); } set { SetValue(OverrideCenterProperty, value); } } // Using a DependencyProperty as the backing store for OverrideCenter. This enables animation, styling, binding, etc... public static readonly DependencyProperty OverrideCenterProperty = DependencyProperty.Register("OverrideCenter", typeof(bool), typeof(ArcII), new FrameworkPropertyMetadata((bool)false, FrameworkPropertyMetadataOptions.AffectsRender)); ///  /// Start angle of arc, using standard coordinates. (Zero is right, CCW positive direction) ///  [Category("Arc")] public double StartAngle { get { return (double)GetValue(StartAngleProperty); } set { SetValue(StartAngleProperty, value); } } // Using a DependencyProperty as the backing store for StartAngle. This enables animation, styling, binding, etc... public static readonly DependencyProperty StartAngleProperty = DependencyProperty.Register("StartAngle", typeof(double), typeof(ArcII), new FrameworkPropertyMetadata(0.0, FrameworkPropertyMetadataOptions.AffectsRender)); ///  /// Length of Arc in degrees. ///  [Category("Arc")] public double SweepAngle { get { return (double)GetValue(SweepAngleProperty); } set { SetValue(SweepAngleProperty, value); } } // Using a DependencyProperty as the backing store for SweepAngle. This enables animation, styling, binding, etc... public static readonly DependencyProperty SweepAngleProperty = DependencyProperty.Register("SweepAngle", typeof(double), typeof(ArcII), new FrameworkPropertyMetadata((double)180, FrameworkPropertyMetadataOptions.AffectsRender)); ///  /// Size of Arc. ///  [Category("Arc")] public double Radius { get { return (double)GetValue(RadiusProperty); } set { SetValue(RadiusProperty, value); } } // Using a DependencyProperty as the backing store for Radius. This enables animation, styling, binding, etc... public static readonly DependencyProperty RadiusProperty = DependencyProperty.Register("Radius", typeof(double), typeof(ArcII), new FrameworkPropertyMetadata(10.0, FrameworkPropertyMetadataOptions.AffectsRender)); [Category("Arc")] public Brush Stroke { get { return (Brush)GetValue(StrokeProperty); } set { SetValue(StrokeProperty, value); } } // Using a DependencyProperty as the backing store for Stroke. This enables animation, styling, binding, etc... public static readonly DependencyProperty StrokeProperty = DependencyProperty.Register("Stroke", typeof(Brush), typeof(ArcII), new FrameworkPropertyMetadata((Brush)Brushes.Black,FrameworkPropertyMetadataOptions.AffectsRender)); [Category("Arc")] public double StrokeThickness { get { return (double)GetValue(StrokeThicknessProperty); } set { SetValue(StrokeThicknessProperty, value); } } // Using a DependencyProperty as the backing store for StrokeThickness. This enables animation, styling, binding, etc... public static readonly DependencyProperty StrokeThicknessProperty = DependencyProperty.Register("StrokeThickness", typeof(double), typeof(ArcII), new FrameworkPropertyMetadata((double)1,FrameworkPropertyMetadataOptions.AffectsRender)); protected override void OnRender(DrawingContext dc) { base.OnRender(dc); Draw(dc); } private void Draw(DrawingContext dc) { Point center = new Point(); if (OverrideCenter) { Rect rect = new Rect(RenderSize); center = Polar.CenterPointFromRect(rect); } else { center = Center; } Point startPoint = Polar.PolarToCartesian(StartAngle, Radius, center); Point endPoint = Polar.PolarToCartesian(StartAngle + SweepAngle, Radius, center); Size size = new Size(Radius, Radius); bool isLarge = (StartAngle + SweepAngle) - StartAngle > 180; List segments = new List(1); segments.Add(new ArcSegment(endPoint, new Size(Radius, Radius), 0.0, isLarge, SweepDirection.Clockwise, true)); List figures = new List(1); PathFigure pf = new PathFigure(startPoint, segments, true); pf.IsClosed = false; figures.Add(pf); Geometry g = new PathGeometry(figures, FillRule.EvenOdd, null); dc.DrawGeometry(null, new Pen(Stroke,StrokeThickness), g); } } 

Usage:

            

Remarques: A) Cela ne fera pas un 360 SweepAngle, pour cela utiliser une ellipse. B) OverrideCenter: Cela place le centre de l’arc au centre de son parent. Notez que les éléments tels qu’une grid pouvant être partitionnée ont toujours un centre qui peut ne pas être la colonne ou la ligne dans laquelle se trouve l’Arc