2013-05-21 14 views
12

Jeśli mam następujące cztery właściwości w moim DataContext mojego elementu CanvasJak narysować łuk o promieniu a start i stop kąt

Point Center 
double Radius 
double StartAngle 
double EndAngle 

mogę narysować łuk bez dodatkowego kodu za?

+0

może [this] (http://stackoverflow.com/questions/6744350/how -can-i-draw-a-circle-sektor-z-ellipse-class) być użytecznym – dkozl

+0

Prawie, ale wciąż muszę ręcznie obliczyć punkty początkowe i końcowe do segmentu łukowego w kodzie za lub widoku modelu. Mogę to zrobić, ale miałem nadzieję, że nie będę musiał. :( – bradgonesurfing

+0

Najprawdopodobniej zdefiniuję niestandardowy kształt łuku, jak tutaj http://stackoverflow.com/questions/12374643/wpf-custom-shape – bradgonesurfing

Odpowiedz

21

Zapewnienie niestandardowego komponentu okazało się najlepszym rozwiązaniem. Używam go w ten sposób w moim kodu

<Controls:Arc Center="{Binding Path=PreviousMousePositionPixels}" 
     Stroke="White" 
     StrokeDashArray="4 4" 
     SnapsToDevicePixels="True" 
     StartAngle="0" 
     EndAngle="{Binding Path=DeltaAngle}" 
     SmallAngle="True" 
     Radius="40"/> 

SmallAngle gdy prawda uczyni mały kąt pomiędzy punktami niezależnie od kolejności StartAngle i EndAngle. Gdy SmallAngle jest fałszywe, łuk jest renderowany w kierunku zgodnym z ruchem wskazówek zegara .

Realizacja jest.

using System; 
using System.Collections.Generic; 
using System.Windows; 
using System.Windows.Documents; 
using System.Windows.Media; 
using System.Windows.Shapes; 

public sealed class Arc : Shape 
{ 
    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(Arc) 
     , new FrameworkPropertyMetadata(new Point(0, 0), FrameworkPropertyMetadataOptions.AffectsRender)); 


    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(Arc) 
     , new FrameworkPropertyMetadata(0.0, FrameworkPropertyMetadataOptions.AffectsRender)); 

    public double EndAngle 
    { 
     get { return (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("EndAngle", typeof(double), typeof(Arc) 
     , new FrameworkPropertyMetadata(Math.PI/2.0, FrameworkPropertyMetadataOptions.AffectsRender)); 

    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(Arc) 
     , new FrameworkPropertyMetadata(10.0, FrameworkPropertyMetadataOptions.AffectsRender)); 



    public bool SmallAngle 
    { 
     get { return (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("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 
     { 

      var a0 = StartAngle < 0 ? StartAngle + 2 * Math.PI : StartAngle; 
      var 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; 
       double t = a1; 
       if ((a1-a0)>Math.PI) 
       { 
        d = SweepDirection.Counterclockwise; 
       } 
       else 
       { 
        d = 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<PathSegment> segments = new List<PathSegment>(1); 
      segments.Add(new ArcSegment(p1, new Size(Radius, Radius), 0.0, large, d, true)); 

      List<PathFigure> figures = new List<PathFigure>(1); 
      PathFigure pf = new PathFigure(p0, segments, true); 
      pf.IsClosed = false; 
      figures.Add(pf); 

      Geometry g = new PathGeometry(figures, FillRule.EvenOdd, null); 
      return g; 
     } 
    } 
} 
+1

Dziękuję za poświęcenie czasu na opublikowanie tego. Oszczędziłem dużo czasu tworzenie animacji łuku Zwróć uwagę, że ten kształt może być użyty w XAML z . – michael

1

Czy mogę zaoferować nieco inne rozwiązanie?

class ArcII:FrameworkElement 
{ 
    /// <summary> 
    /// Center point of Arc. 
    /// </summary> 
    [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)); 

    /// <summary> 
    /// Forces the Arc to the center of the Parent container. 
    /// </summary> 
    [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)); 

    /// <summary> 
    /// Start angle of arc, using standard coordinates. (Zero is right, CCW positive direction) 
    /// </summary> 
    [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)); 

    /// <summary> 
    /// Length of Arc in degrees. 
    /// </summary> 
    [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)); 

    /// <summary> 
    /// Size of Arc. 
    /// </summary> 
    [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<PathSegment> segments = new List<PathSegment>(1); 
     segments.Add(new ArcSegment(endPoint, new Size(Radius, Radius), 0.0, isLarge, SweepDirection.Clockwise, true)); 

     List<PathFigure> figures = new List<PathFigure>(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); 
    } 
} 

Zastosowanie:

<!--Centerd on Parent--> 
    <local:ArcII Center="0,0" 
       OverrideCenter="True" 
       StartAngle="150" 
       SweepAngle="240" 
       Radius="100" 
       Stroke="Red" 
       StrokeThickness="3" 
       /> 

    <!--Centerd on Parent--> 
    <local:ArcII Center="0,0" 
       OverrideCenter="True" 
       StartAngle="150" 
       SweepAngle="240" 
       Radius="95" 
       Stroke="Red" 
       StrokeThickness="3" 
       /> 

    <!--Centerd on Parent--> 
    <local:ArcII Center="0,0" 
       OverrideCenter="True" 
       StartAngle="150" 
       SweepAngle="240" 
       Radius="90" 
       Stroke="Red" 
       StrokeThickness="3" 
       /> 

    <!--Centerd on Point--> 
    <local:ArcII Center="0,150" 
       OverrideCenter="False" 
       StartAngle="270" 
       SweepAngle="180" 
       Radius="100" 
       /> 

    <!--Centerd on Point--> 
    <local:ArcII Center="525,150" 
       OverrideCenter="False" 
       StartAngle="90" 
       SweepAngle="180" 
       Radius="100" 
       /> 

Uwagi: A) nie zrobi 360 SweepAngle, na które używają elipsę. B) OverrideCenter: Spowoduje to umieszczenie środka łuku w środku jego rodzica. Zauważ, że elementy takie jak siatka, która może być podzielona na partycje, nadal mają środek, który może nie być kolumną lub rzędem, w którym znajduje się łuk.

Powiązane problemy