5

I need to draw a line connecting two WPF controls. I have defined a dependency property in my Node objects so if the Node is moved the line still connect the objects.

I have the following example but I'm unable to get it working.

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();

        Node node1 = new Node(myCanvas) { Width = 50, Height = 50 };
        Node node2 = new Node(myCanvas) { Width = 50, Height = 50 };

        Canvas.SetLeft(node1, 0);
        Canvas.SetLeft(node2, 200);

        Canvas.SetTop(node1, 0);
        Canvas.SetTop(node2, 0);

        myCanvas.Children.Add(node1);
        myCanvas.Children.Add(node2);

        Connector conn = new Connector();
        conn.Source = node1.AnchorPoint;
        conn.Destination = node2.AnchorPoint;

        myCanvas.Children.Add(conn);
    }
}

class Node : Control
{
    public static readonly DependencyProperty AnchorPointProperty =
        DependencyProperty.Register(
            "AnchorPoint", typeof(Point), typeof(Node),
                new FrameworkPropertyMetadata(new Point(0, 0), 
                FrameworkPropertyMetadataOptions.AffectsMeasure));

    public Point AnchorPoint
    {
        get { return (Point)GetValue(AnchorPointProperty); }
        set { SetValue(AnchorPointProperty, value); }
    }

    private Canvas mCanvas;

    public Node(Canvas canvas)
    {
        mCanvas = canvas;
        this.LayoutUpdated += Node_LayoutUpdated;
    }

    void Node_LayoutUpdated(object sender, EventArgs e)
    {
        Size size = RenderSize;
        Point ofs = new Point(size.Width / 2, size.Height / 2);
        AnchorPoint = TransformToVisual(this.mCanvas).Transform(ofs);
    }

    protected override void OnRender(DrawingContext drawingContext)
    {
        drawingContext.DrawEllipse(
            Brushes.Red,
            null,
            new Point(Width / 2, Height / 2), Width / 2, Height / 2);
    }
}

public sealed class Connector : UserControl
{
    public static readonly DependencyProperty SourceProperty =
        DependencyProperty.Register(
            "Source", typeof(Point), typeof(Connector),
            new FrameworkPropertyMetadata(default(Point)));

    public Point Source {
        get { return (Point)this.GetValue(SourceProperty); }
        set { this.SetValue(SourceProperty, value); }
    }

    public static readonly DependencyProperty DestinationProperty =
        DependencyProperty.Register(
            "Destination", typeof(Point), typeof(Connector),
                new FrameworkPropertyMetadata(default(Point)));

    public Point Destination {
        get { return (Point)this.GetValue(DestinationProperty); }
        set { this.SetValue(DestinationProperty, value); }
    }

    public Connector()
    {
        LineSegment segment = new LineSegment(default(Point), true);
        PathFigure figure = new PathFigure(default(Point), new[] { segment }, false);
        PathGeometry geometry = new PathGeometry(new[] { figure });
        BindingBase sourceBinding = 
           new Binding { Source = this, Path = new PropertyPath(SourceProperty) };
        BindingBase destinationBinding = 
           new Binding { Source = this, Path = new PropertyPath(DestinationProperty) };
        BindingOperations.SetBinding(
            figure, PathFigure.StartPointProperty, sourceBinding);
        BindingOperations.SetBinding(
            segment, LineSegment.PointProperty, destinationBinding);
        Content = new Path
        {
            Data = geometry,
            StrokeThickness = 5,
            Stroke = Brushes.White,
            MinWidth = 1,
            MinHeight = 1
        };
    }
}
halfer
  • 19,824
  • 17
  • 99
  • 186
Daniel Peñalba
  • 30,507
  • 32
  • 137
  • 219
  • Did you see my [Nodes Editor Sample](http://stackoverflow.com/a/15580293/643085)? It's fully MVVM and implemented the RIGHT way in WPF. You don't need any of those DPs to do what you're looking for. Also, if you need more advanced capabilities (such as Anchor Points, you may want to look at my [Second Sample](http://stackoverflow.com/a/15821573/643085). – Federico Berasategui May 27 '13 at 16:11
  • Where did connector come from? – soandos May 06 '14 at 09:04
  • What is the bool isInput in the constructor Node(Canvas canvas, bool isInput)? – KMC Sep 20 '16 at 02:13
  • @KMC: It's not used, I will edit my post to remote the parameter. – Daniel Peñalba Sep 20 '16 at 07:30

1 Answers1

4

All you have to do to make your example work is to bind conn.Source and .Destination to the nodes' AnchorPoints, or else the Connector just get the AnchorPoints' initial values (0,0), and doesn't listen for further changes:

...

Connector conn = new Connector();
//conn.Source = node1.AnchorPoint;
conn.SetBinding(Connector.SourceProperty, 
                new Binding()
                {
                    Source = node1,
                    Path = new PropertyPath(Node.AnchorPointProperty)
                });
//conn.Destination = node2.AnchorPoint;
conn.SetBinding(Connector.DestinationProperty,
                new Binding()
                {
                    Source = node2,
                    Path = new PropertyPath(Node.AnchorPointProperty)
                });

myCanvas.Children.Add(conn);
Sphinxxx
  • 12,484
  • 4
  • 54
  • 84