0

how can I connect these entity boxes?

enter image description here

the boxes are created by clicking a button and they can be dragged. Now I want to connect them with a line or something.

Tonio
  • 743
  • 1
  • 4
  • 18
terrorista
  • 227
  • 1
  • 15

2 Answers2

1

Here is a quick and dirty implementation:

the class that represents the connection:

public partial class Connection
{
    public MyBox Box1 { get; set; }
    public MyBox Box2 { get; set; }
    public Line Line { get; set; }
}

To create a new connection I've used a basic form: name of source, destination and a button:

<StackPanel Orientation="Horizontal" Grid.Row="3">
    <TextBox Width="100" Text="{Binding From,UpdateSourceTrigger=PropertyChanged,Mode=TwoWay}" />
    <TextBlock Text=" ----> " />
    <TextBox Width="100" Text="{Binding To,UpdateSourceTrigger=PropertyChanged,Mode=TwoWay}" />
    <Button Content="Connect" Click="Button_Click_2" />
</StackPanel>

Here is the handler:

public string From { get; set; }
public string To { get; set; }

private IList<Connection> connections = new List<Connection>();
private void Button_Click_2(object sender, RoutedEventArgs e)
{
    MyBox box1 = boxes.Single(b => b.Header == From);
    MyBox box2 = boxes.Single(b => b.Header == To);

    Connection conn = new Connection { Box1 = box1, Box2 = box2, Line = new Line { StrokeThickness = 1, Stroke = Brushes.Black } };
    connections.Add(conn);

    RefreshLinesPositions();

    panel.Children.Add(conn.Line);
}

And the RefreshLinesPositions code:

private void RefreshLinesPositions()
{
    foreach (Connection conn in connections)
    {
        Point p1 = conn.Box1.TranslatePoint(new Point(0, 0), panel);
        Point p2 = conn.Box2.TranslatePoint(new Point(0, 0), panel);

        double t1 = p1.Y;
        double b1 = p1.Y + conn.Box1.ActualHeight;
        double l1 = p1.X;
        double r1 = p1.X + conn.Box1.ActualWidth;

        double t2 = p2.Y;
        double b2 = p2.Y + conn.Box2.ActualHeight;
        double l2 = p2.X;
        double r2 = p2.X + conn.Box2.ActualWidth;

        if (r1 < l2)
        {
            conn.Line.X1 = r1;
            conn.Line.Y1 = t1 + (b1 - t1) / 2;
            conn.Line.X2 = l2;
            conn.Line.Y2 = t2 + (b2 - t2) / 2;

            conn.Line.Visibility = Visibility.Visible;
        }
        else if (r2 < l1)
        {
            conn.Line.X1 = r2;
            conn.Line.Y1 = t2 + (b2 - t2) / 2;
            conn.Line.X2 = l1;
            conn.Line.Y2 = t1 + (b1 - t1) / 2;

            conn.Line.Visibility = Visibility.Visible;
        }
        else if (b1 < t2)
        {
            conn.Line.X1 = l1 + (r1 - l1) / 2;
            conn.Line.Y1 = b1;
            conn.Line.X2 = l2 + (r2 - l2) / 2;
            conn.Line.Y2 = t2;

            conn.Line.Visibility = Visibility.Visible;
        }
        else if (b2 < t1)
        {
            conn.Line.X1 = l1 + (r1 - l1) / 2;
            conn.Line.Y1 = t1;
            conn.Line.X2 = l2 + (r2 - l2) / 2;
            conn.Line.Y2 = b2;

            conn.Line.Visibility = Visibility.Visible;
        }
        else
        {
            conn.Line.Visibility = System.Windows.Visibility.Collapsed;
        }
    }

And you call RefreshLinesPositions whenever a box is moved:

private void Box_MouseMove(object sender, MouseEventArgs e)
{
    if (draggedBox != null)
    {
        ...

        RefreshLinesPositions();
    }
}

EDIT: for the nodes

The new RefreshLinesPosition version:

private void RefreshLinesPositions()
{
    foreach (Connection conn in connections)
    {
        Point p1 = conn.Box1.TranslatePoint(new Point(0, 0), panel);
        Point p2 = conn.Box2.TranslatePoint(new Point(0, 0), panel);

        double t1 = p1.Y;
        double b1 = p1.Y + conn.Box1.ActualHeight;
        double l1 = p1.X;
        double r1 = p1.X + conn.Box1.ActualWidth;

        double t2 = p2.Y;
        double b2 = p2.Y + conn.Box2.ActualHeight;
        double l2 = p2.X;
        double r2 = p2.X + conn.Box2.ActualWidth;

        if (r1 < l2)
        {
            conn.Line.X1 = r1;
            conn.Line.Y1 = t1 + (b1 - t1) / 2;
            conn.Line.X2 = l2;
            conn.Line.Y2 = t2 + (b2 - t2) / 2;

            conn.Line.Visibility = Visibility.Visible;

            conn.Node1.Text.RenderTransform = new TranslateTransform(r1, t1 + (b1 - t1) / 2);
            conn.Node2.Text.RenderTransform = new TranslateTransform(l2 - conn.Node2.Text.ActualWidth, t2 + (b2 - t2) / 2);
        }
        else if (r2 < l1)
        {
            conn.Line.X1 = l1;
            conn.Line.Y1 = t1 + (b1 - t1) / 2;
            conn.Line.X2 = r2;
            conn.Line.Y2 = t2 + (b2 - t2) / 2;

            conn.Line.Visibility = Visibility.Visible;

            conn.Node1.Text.RenderTransform = new TranslateTransform(l1 - conn.Node1.Text.ActualWidth, t1 + (b1 - t1) / 2);
            conn.Node2.Text.RenderTransform = new TranslateTransform(r2, t2 + (b2 - t2) / 2);
        }
        else if (b1 < t2)
        {
            conn.Line.X1 = l1 + (r1 - l1) / 2;
            conn.Line.Y1 = b1;
            conn.Line.X2 = l2 + (r2 - l2) / 2;
            conn.Line.Y2 = t2;

            conn.Line.Visibility = Visibility.Visible;

            conn.Node1.Text.RenderTransform = new TranslateTransform(l1 + (r1 - l1) / 2, b1);
            conn.Node2.Text.RenderTransform = new TranslateTransform(l2 + (r2 - l2) / 2, t2 - conn.Node2.Text.ActualHeight);
        }
        else if (b2 < t1)
        {
            conn.Line.X1 = l1 + (r1 - l1) / 2;
            conn.Line.Y1 = t1;
            conn.Line.X2 = l2 + (r2 - l2) / 2;
            conn.Line.Y2 = b2;

            conn.Line.Visibility = Visibility.Visible;

            conn.Node1.Text.RenderTransform = new TranslateTransform(l1 + (r1 - l1) / 2, t1 - conn.Node1.Text.ActualHeight);
            conn.Node2.Text.RenderTransform = new TranslateTransform(l2 + (r2 - l2) / 2, b2);
        }
        else
        {
            conn.Line.Visibility = System.Windows.Visibility.Collapsed;
        }
    }
}

The new event handler:

private void Button_Click_2(object sender, RoutedEventArgs e)
{
    MyBox box1 = boxes.Single(b => b.Header == From);
    MyBox box2 = boxes.Single(b => b.Header == To);

    Connection conn = new Connection
    {
        Box1 = box1,
        Box2 = box2,
        Line = new Line { StrokeThickness = 1, Stroke = Brushes.Black },
        Node1 = new Node { Text = new TextBox() },
        Node2 = new Node { Text = new TextBox() }
    };
    connections.Add(conn);

    panel.Children.Add(conn.Line);

    panel.Children.Add(conn.Node1.Text);
    panel.Children.Add(conn.Node2.Text);

    RefreshLinesPositions();
}

I've used this for representing nodes:

public partial class Node
{
    public string Title { get; set; }
    public TextBox Text { get; set; }
}
Pragmateek
  • 13,174
  • 9
  • 74
  • 108
  • i'm getting an error on the "From" and "To", it says it doesnt exist – terrorista Jun 17 '13 at 19:31
  • i click on connect, but nothing happens, maybe I'm doing something wrong – terrorista Jun 17 '13 at 19:51
  • Try to debug and particularly if you reach one of the "conn.Line.Visibility = Visibility.Visible;" – Pragmateek Jun 17 '13 at 20:37
  • when i click on connect, it crashes and appears this error: "InvalidOperationException was unhandled by user code - Sequence contains no matching element" – terrorista Jun 17 '13 at 23:19
  • The code searches for boxes by header name: if you have a box with header "a" and another with "b" then ensure that *From* is "a" and *To* is "b". – Pragmateek Jun 17 '13 at 23:32
  • When you debug and break at the start of Button_Click_2 what are the values of *From* and *To*? – Pragmateek Jun 18 '13 at 10:31
  • in output there is no errors, it only gives an error when i click connect as i mentioned above – terrorista Jun 18 '13 at 15:24
  • Do you bind other properties too? If no check the DataContext, should be the MainWindow. Otherwise, if you don't manage to fix the binding, simply give a name to the TextBoxes and use their Text property directly. :) – Pragmateek Jun 18 '13 at 15:29
  • can you do me somethig like that but for the nodes? to get the nodes always close to the connection :) – terrorista Jun 24 '13 at 22:49
  • i'm getting errors in the translatetransform's "conn.Node1.Text.RenderTransform = new TranslateTransform(r1, t1 + (b1 - t1) / 2);" it says that a TranslateTransform does not contain a constructor that takes two arguments – terrorista Jun 24 '13 at 23:44
  • 1
    @LuisAmaro: SL again :( Try `new TranslateTransform{ X = r1, Y = t1 + (b1 - t1) / 2) }` – Pragmateek Jun 24 '13 at 23:51
0
  1. You have to get the position of each boxes ;
  2. Draw a Path between the boxes ;
  3. Redraw on boxes drag/drop...

Simple sample with two Rectangle :

XAML :

<Canvas x:Name="Root">
    <Rectangle
        x:Name="rect1"
        Fill="Aqua"
        Width="20"
        Height="20"
        Canvas.Left="24"
        Canvas.Top="44" />
    <Rectangle
        x:Name="rect2"
        Fill="Red"
        Width="20"
        Height="20"
        Canvas.Left="119"
        Canvas.Top="73" />
</Canvas>

C# :

ConnectBoxes(Root, rect1, rect2);
....

public void ConnectBoxes(Canvas container, Rectangle boxeSrc, Rectangle boxeDest)
{
    var transform1 = boxeSrc.TransformToVisual(container);
    var transform2 = boxeDest.TransformToVisual(container);

    var lineGeometry = new LineGeometry()
    {
        StartPoint = transform1.Transform(
            new Point(boxeSrc.ActualWidth / 2, boxeSrc.ActualHeight / 2.0)
        ),
        EndPoint = transform2.Transform(
            new Point(boxeDest.ActualWidth / 2.0, boxeDest.ActualHeight / 2.0)
        )
    };
    container.Children.Add(new Path()
    {
        Data = lineGeometry,
        Fill = new SolidColorBrush(Colors.Brown),
        Stroke = new SolidColorBrush(Colors.Brown),

    });
}
Tonio
  • 743
  • 1
  • 4
  • 18
  • this example is good, but how can I do this without premade objects? i'm guessing the better way to do the lines is by clicking a button which permits to draw a line from one object to another.... this example on the link is perfect but it's on wpf and I can't put this on my project :/ http://denisvuyka.wordpress.com/2007/10/13/wpf-draggable-objects-and-simple-shape-connectors/ – terrorista Jun 11 '13 at 21:43
  • do you know how to make the path to adjust with the rectangles when dragging? – terrorista Jun 13 '13 at 16:23