2016-02-24 111 views
2

下面的代碼可以讓我畫帶箭頭的線:WPF定製LineArrow形狀旋轉

public sealed class LineArrow : Shape 
{ 
    #region X1 
    public double X1 
    { 
     get { return (double)GetValue(X1Property); } 
     set { SetValue(X1Property, value); } 
    } 

    // Using a DependencyProperty as the backing store for X1. This enables animation, styling, binding, etc... 
    public static readonly DependencyProperty X1Property = 
     DependencyProperty.Register("X1", typeof(double), typeof(LineArrow), new FrameworkPropertyMetadata(0.0, 
       FrameworkPropertyMetadataOptions.AffectsRender | FrameworkPropertyMetadataOptions.AffectsMeasure)); 
    #endregion 

    #region Y1 
    public double Y1 
    { 
     get { return (double)GetValue(Y1Property); } 
     set { SetValue(Y1Property, value); } 
    } 

    // Using a DependencyProperty as the backing store for Y1. This enables animation, styling, binding, etc... 
    public static readonly DependencyProperty Y1Property = 
     DependencyProperty.Register("Y1", typeof(double), typeof(LineArrow), new FrameworkPropertyMetadata( 
      0.0, FrameworkPropertyMetadataOptions.AffectsRender | FrameworkPropertyMetadataOptions.AffectsMeasure)); 
    #endregion 

    protected override Geometry DefiningGeometry 
    { 
     get 
     { 
      var lineStart = new Point(X1, Y1); 
      var lineEnd = new Point(this.ActualWidth, this.ActualHeight); 
      var lineAngle = Math.Atan2(this.ActualHeight, this.ActualWidth); 

      RotateTransform rotation = new RotateTransform() { Angle = lineAngle * 180/Math.PI, CenterX = 0.5, CenterY = 0.5 }; 
      TranslateTransform translate = new TranslateTransform(lineEnd.X, lineEnd.Y); 

      StreamGeometry streamGeometry = new StreamGeometry(); 
      using(StreamGeometryContext geometryContext = streamGeometry.Open()) 
      { 
       geometryContext.BeginFigure(lineStart, true, true); 
       geometryContext.LineTo(lineEnd, true, true); 

       //left arrow 
       geometryContext.BeginFigure(lineStart, true, true); 
       geometryContext.PolyLineTo(new List<Point>() 
       { 
        rotation.Transform(new Point(0, -15)), 
        rotation.Transform(new Point(-15, 0)), 
        rotation.Transform(new Point(0, 15)) 
       }, true, true); 

       //right arrow 
       geometryContext.BeginFigure(lineEnd, true, true); 
       geometryContext.PolyLineTo(new List<Point>() 
       { 
        translate.Transform(rotation.Transform(new Point(0, -15))), 
        translate.Transform(rotation.Transform(new Point(15,0))), 
        translate.Transform(rotation.Transform(new Point(0, 15))) 
       }, true, true); 
      } 

      streamGeometry.Freeze(); 
      return streamGeometry;    
     } 
    } 
} 

這裏是它如何工作:

enter image description here

我怎樣才能使箭頭維護自己的在旋轉時的初始尺寸爲15x15,特別是在接近90度180度的角度時?

回答

2

讓我們考慮幾何背景。你有一箇中心,你有一個箭頭。角度改變時箭頭的長度不應改變。我們剛剛描述的形狀是什麼?是的,你是對的,這是一個圓圈。現在,圈子的屬性是什麼?它的中心是點(X1,Y1)您正在使用作爲穩定點和它的半徑是箭頭的初始長度。基本上,你打算根據中心,半徑和角度找到終點。

我們假設角度是alpha。在這種情況下,(X2,Y2)座標你正在尋找的是:

X2 = X1 +半徑* COS(阿爾法)

Y2 = Y1 +半徑* SIN(阿爾法)

+0

不應該RotateTransform照顧應用sin到y和cos到x的數學嗎? – sam

+0

@sam,我不是wpf的專家,但我碰巧知道所需座標的公式如何。作爲wpf的局外人,我能做的最多的是解釋理論背景。這樣,我相信你將能夠在你喜歡的風格(帶或不帶RotateTransform)中編寫正確的代碼。 –

0

你應該定義X1Y1X2Y2屬性,如Line一樣。

然後可以像下面顯示的那樣完成繪圖,可能會將箭頭尺寸的常量替換爲另一個依賴項屬性的值。它首先用連接起點和終點的矢量長度繪製一條帶有箭頭的水平線,然後適當旋轉它。

protected override Geometry DefiningGeometry 
{ 
    get 
    { 
     var vector = new Point(X2, Y2) - new Point(X1, Y1); 
     var angle = Vector.AngleBetween(new Vector(1, 0), vector); 
     var geometry = new StreamGeometry(); 

     using (var sgc = geometry.Open()) 
     { 
      // left arrow 
      sgc.BeginFigure(new Point(X1, Y1), true, true); 
      sgc.LineTo(new Point(X1 + 15, Y1 - 10), true, true); 
      sgc.LineTo(new Point(X1 + 15, Y1 + 10), true, true); 

      // right arrow 
      sgc.BeginFigure(new Point(X1 + vector.Length, Y1), true, true); 
      sgc.LineTo(new Point(X1 + vector.Length - 15, Y1 - 10), true, true); 
      sgc.LineTo(new Point(X1 + vector.Length - 15, Y1 + 10), true, true); 

      // line 
      sgc.BeginFigure(new Point(X1 + 15, Y1), false, false); 
      sgc.LineTo(new Point(X1 + vector.Length - 15, Y1), true, true); 
     } 

     geometry.Transform = new RotateTransform(angle, X1, Y1); 
     geometry.Freeze(); 

     return geometry; 
    } 
} 
+0

您的代碼解決了部分問題。基本上它以正確的方式處理旋轉,我爲此感謝你,但如果你試圖使該形狀填充父容器,則箭頭會相應地增長。在我的特殊情況下,我將這種形狀的寬度和高度綁定到父寬度和高度。任何想法? – sam

+0

然後我不確定你爲什麼擁有X1和Y1屬性。你可以簡單地從'(0,0)'繪製到'(ActualWidth,ActualHeight)',而不是從'(X1,Y1)'繪製到'(X2,Y2)'。只需在我的代碼示例中替換它即可。 – Clemens

+0

但是如果我這樣做,我們又回到原點,導致箭頭縮小。我其實在我的問題代碼中做到了這一點! – sam