Search code examples
wpfdrawing

WPF how to draw an arrow between 2 component


I created a custom tree for my program (cannot use the treeview for various reasons), but it's not really clear which component is parent of which kid. I would like to add arrow that could be called like this :

var arrow = CreateArrowBetween(c1,c2)

But i have not idea how, can anyone help me or give me a lead ? Ps : for now the element are on a grid


Solution

  • The following example shows how to draw a centered arrow between two FrameworkElement elements hosted in a container e.g., Grid.

    It's very basic and needs to be improved in order to detect if the elements are stacked or next to each other. Right now the algorithm assumes stacked elements and positions the arrow horizontally centered on the bottom/top edges of the elements.
    If elements are next to each other you may want to conect the left/right edges. MainWindow.xaml

    <Window>
      <Grid x:Name="Container">
        <TextBox x:Name="TextBox" Text="Some text" Height="20" />
        <TextBlock x:Name="TextBlock" Margin="0,100,0,0" Text="More text" />
      </Grid>
    </Window>
    

    MainWindow.xaml.cs

    partial class MainWindow : Window
    {
      public MainWindow()
      {
        InitializeComponent();
        this.Loaded += OnLoaded;
      }
    
      private void OnLoaded(object sender, RoutedEventArgs e)
      {
        CreateArrowBetween(this.TextBlock, this.TextBox, this.Container);
      }
    
      // The arrow will point from start to end
      private void CreateArrowBetween(FrameworkElement startElement, FrameworkElement endElemend, Panel parentContainer)
      {
        SolidColorBrush arrowBrush = Brushes.Red;
    
        // Center the line horizontally and vertically.
        // Get the positions of the controls that should be connected by a line.
        Point centeredArrowStartPosition = startElement.TransformToAncestor(parentContainer)
          .Transform(new Point(startElement.ActualWidth / 2, startElement.ActualHeight / 2));
    
        Point centeredArrowEndPosition = endElemend.TransformToAncestor(parentContainer)
          .Transform(new Point(endElemend.ActualWidth / 2, endElemend.ActualHeight / 2));
    
        // Draw the line between two controls
        var arrowLine = new Line()
        {
          Stroke = Brushes.Red,
          StrokeThickness = 2,
          X1 = centeredArrowStartPosition.X,
          Y2 = centeredArrowEndPosition.Y,
          X2 = centeredArrowEndPosition.X,
          Y1 = centeredArrowStartPosition.Y
        };
        parentContainer.Children.Add(
          arrowLine);
    
        // Create the arrow tip of the line. The arrow has a width of 8px and a height of 8px,
        // where the position of arrow tip and the line's end are the same
        var arrowLineTip = new Polygon() {Fill = Brushes.Red};
        var leftRectanglePoint = new Point(centeredArrowEndPosition.X - 4, centeredArrowEndPosition.Y + 8);
        var rightRectanglePoint = new Point(
          centeredArrowEndPosition.X + 4,
          centeredArrowEndPosition.Y + 8);
        var rectangleTipPoint = new Point(centeredArrowEndPosition.X, centeredArrowEndPosition.Y);
        var myPointCollection = new PointCollection
        {
          leftRectanglePoint, 
          rightRectanglePoint, 
          rectangleTipPoint
        };
        arrowLineTip.Points = myPointCollection;
        parentContainer.Children.Add(
          arrowLineTip);
      }
    }