2016-02-24 46 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