如何在Silverlight中绘制箭头

5

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

大致上我需要的是:

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

我试图对Line进行子类化并在末尾添加几行,但该类是密封的。

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

4个回答

7

Charles Petzold编写了一个用于在WPF中实现此功能的库。至少,逻辑应该可以转移到Silverlight。它使用Polylines和Paths,并且很容易进行移植。

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)));

你将拥有带有1像素描边线的每条线末端带有20像素描边三角形。

--编辑--

@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>

然后,将“Update”方法更改为获取线条的角度,并将帽子旋转到该角度:
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(或形状)继承。Line是密封的,而Shape不会进行任何绘制。我认为运行时负责绘图,因为每个Shape类都有一个不同的“已知标识符”。 - Joseph Liberty
干得好。看起来箭头指向东西方向,即使线路是从西北到东南。如何才能使箭头指向与线路相同的方向? - Number8
@Number8 - 谢谢,不确定你的意思。你能发布一下你正在使用实例化用户控件的代码吗? - Timothy Lee Russell
复制并粘贴了您的代码,创建了这样一个箭头:new Dagger1(new Point(20, 200), new Point(100, 450))。线条从西北到东南延伸,但箭头指向东西方向。 - Number8
@Number8 - 根据线的方向,使用RotateTransform更新了用户控件。 - Timothy Lee Russell

5
这个简单的方法还可以创建箭头,对我很有效。
    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;
    }

0

所有这些运行时和动画线

//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;
        }



    }

0

但是三角形的宽度与线的宽度相同,而线的宽度为1像素,因此它不显示。 - Joseph Liberty

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接