2009-10-13 17 views
5

Necesito dibujar una flecha entre los controles en un lienzo. Actualmente estoy usando el objeto Línea, pero no tiene forma de dibujar un triángulo al final de la línea.Cómo dibujar una flecha en Silverlight

Esto es aproximadamente lo que necesito:

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

yo estaba tratando de subclase de línea y añadir un par de líneas al final, pero la clase se sella.

¿Cómo construirías un control personalizado que dibuja una flecha entre X1, Y1 y X2, Y2?

Gracias

Respuesta

7

Charles Petzold escribió una biblioteca para hacer esto en WPF. La lógica, al menos, debería ser transferible a Silverlight. Utiliza Polylines y Paths y debería ser fácil de portar.

Lines with Arrows @ Petzold Book Blog

--EDIT--

Ok - aquí hay otra manera de hacerlo:

Crear un control de usuario:

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

con la siguiente código:

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

llamada así:

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

y tendrá líneas de trazo 1px con triángulos de carrera 20 píxeles en el final de cada línea.

--EDIT--

@ Number8 tenía una pregunta acerca de cómo modificar el control de usuario de modo que las tapas se apuntan en la misma dirección que la línea.

Modificar el Xaml del control de usuario, así:

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

A continuación, cambiar el método de "Update" para obtener el ángulo de la línea y girar las tapas a ese ángulo:

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; 
} 
+0

Ese es un código interesante, pero en Silverlight no puedo heredar de Line (o forma para el caso) como lo hizo Petzold en WPF.La línea está sellada y Shape no hace ningún dibujo. Creo que el tiempo de ejecución es el encargado de dibujar porque cada una de las clases de Shape tiene un "identificador conocido" diferente. –

+0

El segundo ejemplo funcionó. Gracias. –

+0

Buen trabajo. Parece que las flechas apuntan de este a oeste, incluso cuando la línea corre nw - se. ¿Qué se necesita para que las puntas de flecha apunten en la misma dirección en que se está ejecutando la línea? – Number8

5

Este método simple también crea una flecha y funcionó para mí.

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

Todo esto en tiempo de ejecución y la animación Línea

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



    } 
Cuestiones relacionadas