J. Doe
J. Doe

Reputation: 13

How to connect ellipses using lines in WPF?

I am developing an application to visualize routers in my network. I have four or less ports in each router. I have a list of routers with their X and Y coordinates on the canvas and they look good.

I need to link them together based on the port and the next router using a line.

I have created a cable class which has two routers and a two ports. I don't know how to add them to the Itemescontrol list of my window.

Cable.cs
public class Cable
    {

        public Cable()
        {
        }

        public Cable(int rtr1ID, int rtr1Port, int rtr2ID, int rtr2Port)
        {
            this.rtr1ID = rtr1ID;
            this.rtr2ID = rtr2ID;
            this.rtr1Port = rtr1Port;
            this.rtr2Port = rtr2Port;
        }

        private int rtr1ID;
        public int Router1ID
        {
            get { return rtr1ID; }
            set { rtr1ID = value; }
        }

        private int rtr2ID;
        public int Router2ID
        {
            get { return rtr2ID; }
            set { rtr2ID = value; }
        }

        private int rtr1Port;
        public int Router1Port
        {
            get { return rtr1Port; }
            set { rtr1Port = value; }
        }

        private int rtr2Port;
        public int Router2Port
        {
            get { return rtr2Port; }
            set { rtr2Port = value; }
        }
    }
CableService.cs

    public class CableService
    {
        private static List<Cable> cables;

        public CableService()
        {
            cables = new List<Cable>()
            {
                new Cable{ Router1ID=1, Router1Port=1, Router2ID=2, Router2Port=4},
                new Cable{ Router1ID=2, Router1Port=3, Router2ID=5, Router2Port=2},
                new Cable{ Router1ID=5, Router1Port=3, Router2ID=3, Router2Port=2},
                new Cable{ Router1ID=4, Router1Port=4, Router2ID=1, Router2Port=1},
            };
        }

        public List<Cable> GetCables()
        {
            return cables;
        }
    }
Router.cs
public class Router : INotifyPropertyChanged
    {
        public Router()
        {
        }

        public Router(int x, int y, int id)
        {
            this.x = x;
            this.y = y;
            this.id = id;
        }

        private int x;
        public int X
        {
            get { return x; }
            set { x = value; OnPropertyChanged("X"); }
        }

        private int id;
        public int ID
        {
            get { return id; }
            set { id = value; OnPropertyChanged("ID"); }
        }


        private int y;
        public int Y
        {
            get { return y; }
            set { y = value; OnPropertyChanged("Y"); }
        }

        public event PropertyChangedEventHandler PropertyChanged;
        private void OnPropertyChanged(string popName)
        {
            if(PropertyChanged!=null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(popName));
            }
        }

    }
RouterService.cs
public class RoutersServcie
    {
        private static List<Router> rtrList;
        public RoutersServcie()
        {
            rtrList = new List<Router>()
            {
                new Router{X=5, ID=1, Y=2},
                new Router{X=50, ID=2, Y=20},
                new Router{X=90, ID=3, Y=4},
                new Router{X=10, ID=4, Y=90},
                new Router{X=250, ID=5, Y=15},
            };
        }

        public List<Router> GetRouters()
        {
            return rtrList;
        }
MainWindow.xaml
<Window x:Class="RoutersMVVM.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:RoutersMVVM"
        mc:Ignorable="d"
        Title="MainWindow" Height="Auto" Width="Auto">
    <DockPanel>
        <Canvas DockPanel.Dock="Top">
            <ItemsControl Name="icCircles">
                <ItemsControl.ItemsPanel>
                    <ItemsPanelTemplate>
                        <Canvas Background="Transparent" Width="Auto" Height="Auto"/>
                    </ItemsPanelTemplate>
                </ItemsControl.ItemsPanel>
                <ItemsControl.ItemContainerStyle>
                    <Style TargetType="ContentPresenter">
                        <Setter Property="Canvas.Left" Value="{Binding Path=X,Mode=TwoWay}"  />
                        <Setter Property="Canvas.Top" Value="{Binding Path=Y,Mode=TwoWay}" />
                    </Style>
                </ItemsControl.ItemContainerStyle>
                <ItemsControl.ItemTemplate>
                    <DataTemplate>
                        <Ellipse Width="30" Height="30"  Fill="Black" Stroke="Black" />
                    </DataTemplate>
                </ItemsControl.ItemTemplate>
            </ItemsControl>
        </Canvas>
        <StackPanel DockPanel.Dock="Bottom" Height="20">
            <Button Click="Button_Click">Click me</Button>
        </StackPanel>
    </DockPanel>
</Window>
MainWindow.xaml.cs
public partial class MainWindow : Window
    {
        List<Router> line;
        public MainWindow()
        {
            InitializeComponent();
            line = new RoutersServcie().GetRouters();
            icCircles.ItemsSource = line;
        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            icCircles.ItemsSource = null;
            int x = new Random().Next(0,500);
            int y = new Random(x).Next(0,500);
            int nextId = line.Count()+1;
            line.Add(new Router(y, x, nextId));
            icCircles.ItemsSource = line;
        }
    }

Upvotes: 1

Views: 229

Answers (1)

Clemens
Clemens

Reputation: 128061

You may draw lines in a second ItemsControl below the one with the circles.

With a view model like

public class ViewModel
{
    public ObservableCollection<PointItem> Points { get; }
        = new ObservableCollection<PointItem>();
}

public class PointItem
{
    public double X { get; set; }
    public double Y { get; set; }
}

the following XAML declares two ItemsControl on top of each other in a Canvas parent.

The ItemsControl for the lines uses an ItemContainerStyle with a Line element that has its X1 and Y1 properties bound to the X and Y values of the previous data item, which is easily possibly with the RelativeSource={RelativeSource Mode=PreviousData} binding source mode. The control hides the "first" line by a Trigger on the ItemsControl.AlternationIndex attached properties.

Note that the other control uses Path elements with an EllipseGeometry instead of Ellipse elements to make sure that the circles are centered at their respective position.

<Canvas>

    <ItemsControl ItemsSource="{Binding Points}" AlternationCount="1000000">
        <ItemsControl.ItemsPanel>
            <ItemsPanelTemplate>
                <Canvas/>
            </ItemsPanelTemplate>
        </ItemsControl.ItemsPanel>
        <ItemsControl.ItemContainerStyle>
            <Style TargetType="ContentPresenter">
                <Style.Triggers>
                    <Trigger Property="ItemsControl.AlternationIndex" Value="0">
                        <Setter Property="Visibility" Value="Hidden"/>
                    </Trigger>
                </Style.Triggers>
            </Style>
        </ItemsControl.ItemContainerStyle>
        <ItemsControl.ItemTemplate>
            <DataTemplate>
                <Canvas>
                    <Line Stroke="Green" StrokeThickness="3"
                          X1="{Binding X,
                               RelativeSource={RelativeSource PreviousData}}"
                          Y1="{Binding Y,
                               RelativeSource={RelativeSource PreviousData}}"
                          X2="{Binding X}"
                          Y2="{Binding Y}"/>
                </Canvas>
            </DataTemplate>
        </ItemsControl.ItemTemplate>
    </ItemsControl>

    <ItemsControl ItemsSource="{Binding Points}">
        <ItemsControl.ItemsPanel>
            <ItemsPanelTemplate>
                <Canvas/>
            </ItemsPanelTemplate>
        </ItemsControl.ItemsPanel>
        <ItemsControl.ItemContainerStyle>
            <Style TargetType="ContentPresenter">
                <Setter Property="Canvas.Left" Value="{Binding X}"  />
                <Setter Property="Canvas.Top" Value="{Binding Y}" />
            </Style>
        </ItemsControl.ItemContainerStyle>
        <ItemsControl.ItemTemplate>
            <DataTemplate>
                <Canvas>
                    <Path Fill="Red">
                        <Path.Data>
                            <EllipseGeometry RadiusX="15" RadiusY="15"/>
                        </Path.Data>
                    </Path>
                </Canvas>
            </DataTemplate>
        </ItemsControl.ItemTemplate>
    </ItemsControl>

</Canvas>

Upvotes: 1

Related Questions