【问题标题】:How to draw an arrow in Silverlight如何在 Silverlight 中绘制箭头
【发布时间】:2009-10-13 22:23:23
【问题描述】:

我需要在画布中的控件之间绘制一个箭头。目前我正在使用Line 对象,但它没有办法在行尾绘制三角形。

这大概就是我需要的:

[TextBox] <----- [Button]

我试图继承 Line 并在末尾添加几行,但该类是密封的。

您将如何构建一个在 X1,Y1 和 X2,Y2 之间绘制箭头的自定义控件?

【问题讨论】:

    标签: canvas silverlight


    【解决方案1】:

    Charles Petzold 在 WPF 中为此编写了一个库。至少,逻辑应该可以转移到 Silverlight。它使用折线和路径,应该很容易移植。

    Lines with Arrows @ Petzold Book Blog

    --编辑--

    好的——这是另一种方法:

    创建用户控件:

    <UserControl x:Class="ArrowsAndDaggersLibrary.ArrowsAndDaggersUC"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
        <Canvas x:Name="LayoutRoot">
            <Line x:Name="Cap" />
            <Line x:Name="Connector" />
            <Line x:Name="Foot" />
        </Canvas>
    </UserControl>
    

    使用以下代码:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Net;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Documents;
    using System.Windows.Input;
    using System.Windows.Media;
    using System.Windows.Media.Animation;
    using System.Windows.Shapes;
    
    namespace ArrowsAndDaggersLibrary
    {
        public partial class ArrowsAndDaggersUC : UserControl
        {
            private Point startPoint;
            public Point StartPoint
            {
                get { return startPoint; }
                set
                {
                    startPoint = value;
                    Update();
                }
            }
    
            private Point endPoint;
            public Point EndPoint
            {
                get { return endPoint; }
                set { 
                    endPoint = value;
                    Update();
                }
            }
    
            public ArrowsAndDaggersUC()
            {
                InitializeComponent();
            }
    
            public ArrowsAndDaggersUC(Point StartPoint, Point EndPoint)
            {
                InitializeComponent();
                startPoint = StartPoint;
                endPoint = EndPoint;
                Update();
            }
    
            private void Update()
            {
                //reconfig
                Connector.X1 = startPoint.X;
                Connector.Y1 = startPoint.Y;
                Connector.X2 = endPoint.X;
                Connector.Y2 = endPoint.Y;
                Connector.StrokeThickness = 1;
                Connector.Stroke = new SolidColorBrush(Colors.Black);
    
                Cap.X1 = startPoint.X;
                Cap.Y1 = startPoint.Y;
                Cap.X2 = startPoint.X;
                Cap.Y2 = startPoint.Y;
                Cap.StrokeStartLineCap = PenLineCap.Triangle;
                Cap.StrokeThickness = 20;
                Cap.Stroke = new SolidColorBrush(Colors.Black);
    
                Foot.X1 = endPoint.X;
                Foot.Y1 = endPoint.Y;
                Foot.X2 = endPoint.X;
                Foot.Y2 = endPoint.Y;
                Foot.StrokeEndLineCap = PenLineCap.Triangle;
                Foot.StrokeThickness = 20;
                Foot.Stroke = new SolidColorBrush(Colors.Black);
            }
        }
    }
    

    这样称呼它:

    LayoutRoot.Children.Add(new ArrowsAndDaggersUC(new Point(200, 200), new Point(300, 400)));
    

    你会有 1px 的笔触线,每条线的末端有 20px 的笔触三角形。

    --编辑--

    @Number8 有一个关于如何修改用户控件以使大写与线条指向同一方向的问题。

    像这样修改用户控件的 Xaml:

    <UserControl x:Class="ArrowsAndDaggersLibrary.ArrowsAndDaggersUC"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
        <Canvas x:Name="LayoutRoot">
            <Line x:Name="Cap">
                <Line.RenderTransform>
                    <RotateTransform x:Name="CapRotateTransform" />
                </Line.RenderTransform>
            </Line>
            <Line x:Name="Connector" />
            <Line x:Name="Foot">
                <Line.RenderTransform>
                    <RotateTransform x:Name="FootRotateTransform" />
                </Line.RenderTransform>
            </Line>
        </Canvas>
    </UserControl>
    

    然后,更改“更新”方法以获取线的角度并将帽旋转到该角度:

    private void Update()
    {
    
        double angleOfLine = Math.Atan2((endPoint.Y - startPoint.Y), (endPoint.X - startPoint.X)) * 180 / Math.PI;
    
        Connector.X1 = startPoint.X;
        Connector.Y1 = startPoint.Y;
        Connector.X2 = endPoint.X;
        Connector.Y2 = endPoint.Y;
        Connector.StrokeThickness = 1;
        Connector.Stroke = new SolidColorBrush(Colors.Black);
    
        Cap.X1 = startPoint.X;
        Cap.Y1 = startPoint.Y;
        Cap.X2 = startPoint.X;
        Cap.Y2 = startPoint.Y;
        Cap.StrokeStartLineCap = PenLineCap.Triangle;
        Cap.StrokeThickness = 20;
        Cap.Stroke = new SolidColorBrush(Colors.Black);
    
        CapRotateTransform.Angle = angleOfLine;
        CapRotateTransform.CenterX = startPoint.X;
        CapRotateTransform.CenterY = startPoint.Y;
    
        Foot.X1 = endPoint.X;
        Foot.Y1 = endPoint.Y;
        Foot.X2 = endPoint.X;
        Foot.Y2 = endPoint.Y;
        Foot.StrokeEndLineCap = PenLineCap.Triangle;
        Foot.StrokeThickness = 20;
        Foot.Stroke = new SolidColorBrush(Colors.Black);
    
        FootRotateTransform.Angle = angleOfLine;
        FootRotateTransform.CenterX = endPoint.X;
        FootRotateTransform.CenterY = endPoint.Y;
    }
    

    【讨论】:

    • 这是一些有趣的代码,但在 Silverlight 中,我不能像 Petzold 在 WPF 中那样从 Line(或形状)继承。线是密封的,Shape 不做任何绘图。我认为运行时负责绘图,因为每个 Shape 类都有不同的“已知标识符”。
    • 干得好。看起来箭头指向东西方向,即使线路运行 nw - se 也是如此。怎样才能使箭头指向线路运行的同一方向?
    • @Number8 - 谢谢,不知道你的意思。你能发布你用来实例化用户控件的代码吗?
    • 剪切'n粘贴您的代码,创建一个像这样的箭头:new Dagger1(new Point(20, 200), new Point(100, 450)。线运行西北-东南,但箭头指向东方-西。
    • @Number8 - 使用基于线方向的 RotateTransform 更新了用户控件。
    【解决方案2】:

    这个简单的方法还可以创建一个箭头,它对我有用。

        private static Shape DrawArrow(Point p1, Point p2)
        {
            GeometryGroup lineGroup = new GeometryGroup();
    
            double theta = Math.Atan2((p2.Y - p1.Y),(p2.X - p1.X)) * 180 / Math.PI;
    
            PathGeometry pathGeometry = new PathGeometry();
            PathFigure pathFigure = new PathFigure();
            pathFigure.StartPoint = p1;
    
            Point lpoint = new Point(p1.X + 2, p1.Y + 10);
            Point rpoint = new Point(p1.X - 2, p1.Y + 10);
            LineSegment seg1 = new LineSegment();
            seg1.Point = lpoint;
            pathFigure.Segments.Add(seg1);
    
            LineSegment seg2 = new LineSegment();
            seg2.Point = rpoint;
            pathFigure.Segments.Add(seg2);
    
            LineSegment seg3 = new LineSegment();
            seg3.Point = p1;
            pathFigure.Segments.Add(seg3);
    
            pathGeometry.Figures.Add(pathFigure);
            RotateTransform transform = new RotateTransform();
            transform.Angle = theta - 90;
            transform.CenterX = p1.X;
            transform.CenterY = p1.Y;
            pathGeometry.Transform = transform;
            lineGroup.Children.Add(pathGeometry);
    
            LineGeometry connectorGeometry = new LineGeometry();
            connectorGeometry.StartPoint = p1;
            connectorGeometry.EndPoint = p2;
            lineGroup.Children.Add(connectorGeometry);
            Path path = new Path();
            path.Data = lineGroup;
            return path;
        }
    

    【讨论】:

      【解决方案3】:

      你可以试试三角笔帽;我用它来做类似的事情

      http://msdn.microsoft.com/en-us/library/system.windows.media.penlinecap(VS.95).aspx

      【讨论】:

      • 但是三角形是线条的宽度,线条是1px,所以它不显示。
      【解决方案4】:

      所有这些运行时和动画线

      //animation
      public class Cls_Barriere
          {                       
              // animazione periferica
              public static void LineAnimation(Line _line,String _colore)
              {
      
                  Storyboard result = new Storyboard();
                  Duration duration = new Duration(TimeSpan.FromSeconds(2));
      
                  ColorAnimation animation = new ColorAnimation();
                  animation.RepeatBehavior = RepeatBehavior.Forever;
                  animation.Duration = duration;
                  switch (_colore.ToUpper())
                  {
                      case "RED": 
                          animation.From = Colors.Red;
                          break;
                      case "ORANGE": 
                          animation.From = Colors.Orange;
                          break;
                      case "YELLOW": 
                          animation.From = Colors.Yellow;
                          break;
                      case "GRAY": 
                          animation.From = Colors.DarkGray;
                          break;
                      default: 
                          animation.From = Colors.Green;
                          break;
                  }
      
                  animation.To = Colors.Gray;
                  Storyboard.SetTarget(animation, _line);
                  Storyboard.SetTargetProperty(animation, new PropertyPath("(Line.Stroke).(SolidColorBrush.Color)"));
                  result.Children.Add(animation);
                  result.Begin();
      
              }
          }
      
      
      
      public partial class MainPage : UserControl
          {
              private Point startPoint;
              private Point endPoint;
      
              // canvas event onmouse click to start drawing runtime a line
              public MainPage()
              {
                  InitializeComponent();
                  Canvas.MouseLeftButtonDown += Canvas_MouseLeftButtonDown;
                  Canvas.MouseLeftButtonUp += Canvas_MouseLeftButtonUp;
      
              }
      
              // on muose up drawing line and add canvas all references
              void Canvas_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
              {
                  endPoint = new Point();
                  endPoint.X = e.GetPosition(this.Canvas).X;
                  endPoint.Y = e.GetPosition(this.Canvas).Y;
                  Line LineCap = new Line();
                  Line LineFoot = new Line();
                  Line LineConnect = new Line();
                  RotateTransform FootRotateTransform = new RotateTransform();
                  RotateTransform CapRotateTransform = new RotateTransform();
      
      
                  LineConnect.Stroke = new SolidColorBrush(Colors.White);
                  LineConnect.StrokeThickness = 5;
                  LineConnect.StrokeStartLineCap = PenLineCap.Round;
                  LineConnect.StrokeEndLineCap = PenLineCap.Round;
                  LineConnect.X1 = startPoint.X;
                  LineConnect.Y1 = startPoint.Y;
                  LineConnect.X2 = endPoint.X;
                  LineConnect.Y2 = endPoint.Y;
      
                  LineCap.X1 = startPoint.X;
                  LineCap.X2 = startPoint.X;
                  LineCap.Y1 = startPoint.Y;
                  LineCap.Y2 = startPoint.Y;
                  LineCap.StrokeThickness = 20;
                  LineCap.StrokeStartLineCap = PenLineCap.Round;
                  LineCap.Stroke = new SolidColorBrush(Colors.White);
                  LineFoot.StrokeThickness = 20;
      
                  LineFoot.X1 = endPoint.X;
                  LineFoot.X2 = endPoint.X;
                  LineFoot.Y1 = endPoint.Y;
                  LineFoot.Y2 = endPoint.Y;
                  LineFoot.StrokeEndLineCap = PenLineCap.Triangle;
                  LineFoot.Stroke = new SolidColorBrush(Colors.White);
                  Double angleOfLine = new Double();
                  angleOfLine = Math.Atan2((LineConnect.Y2 - LineConnect.Y1), (LineConnect.X2 - LineConnect.X1)) * 180 / Math.PI;
                  FootRotateTransform.Angle = angleOfLine;
                  FootRotateTransform.CenterX = endPoint.X;
                  FootRotateTransform.CenterY = endPoint.Y;
      
                  CapRotateTransform.Angle = angleOfLine;
                  CapRotateTransform.CenterX = startPoint.X;
                  CapRotateTransform.CenterY = startPoint.Y;
                  LineFoot.RenderTransform = FootRotateTransform;
                  LineCap.RenderTransform = CapRotateTransform;
      
                  LineConnect.Loaded += _line_Loaded;
                  LineCap.Loaded += _line_Loaded;
                  LineFoot.Loaded += _line_Loaded;
                  Canvas.Children.Add(LineConnect);
                  Canvas.Children.Add(LineCap);
                  Canvas.Children.Add(LineFoot);
              }
              //load animation color
              void _line_Loaded(object sender, RoutedEventArgs e)
              {
                  Cls_Barriere.LineAnimation(sender as Line, "RED");
              }
              // add canvas lines
              void Canvas_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
              {
                  startPoint = new Point();
                  startPoint.X = e.GetPosition(this.Canvas).X;
                  startPoint.Y = e.GetPosition(this.Canvas).Y;
              }
      
      
      
          }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多