Reputation: 660
So I'm working with a system of switches and bulbs, which are attached together by wires. I'm supposed to make the switches draggable, in such a way that the wires also move. Since you cannot update the points directly in a PointCollection (I think they're a struct I think?), I've figured out a way, but this seems VERY verbose, and I figure there must be a better way to do this.
Basically I retrieve the points, do the manipulation, clear the collection, and add the new points:
Point firstPoint = new Point(firstPointCollection[0].X, firstPointCollection[0].Y + offset.Y);
Point secondPoint = new Point(firstPointCollection[1].X + offset.X, firstPointCollection[1].Y + offset.Y);
Point thirdPoint = new Point(firstPointCollection[2].X + offset.X, firstPointCollection[2].Y + offset.Y);
firstPointCollection.Clear();
firstPointCollection.Add(firstPoint);
firstPointCollection.Add(secondPoint);
firstPointCollection.Add(thirdPoint);
In a system with multiple wires, which all consist of multiple points, this very quickly gets very tedious to write. This must all be done in C#, but if there is a better way to do with this with Data Binding of some sort, please let me know.
Upvotes: 0
Views: 3838
Reputation: 1744
I really like the accepted answer - an upvote well deserved - because it was well written and really works. But, I thought it would be worth noting: if you intend to use it for many quick changing values, updating a PointCollection
will perform better. See How to draw a simple (line) graph?.
Regarding the code and it's verbosity, maybe this would have (7 years ago) helped:
firstPointCollection = new PointCollection(firstPointCollection.Select(p=>new Point(p.X + offset.X, p.Y + offset.Y)));
Cheers ;o)
Upvotes: 0
Reputation: 4680
First of all, the Offset() method
As Amnestic pointed out even though Point is a struct you can mutate it through the Offset method. However since he is using a PointCollection and not an array of Points when you use the indexer you will get a copy of the struct, not the original.
If the points were in a Point[] you could just use this:
firstPointCollection[0].Offset(x, y)
However because we are using a PointCollection you need to set the indexer as such:
firstPointCollection[0] = firstPointCollection[0].Offset(x, y)
The discussion over whether structs should be mutable or not is a whole other kettle of fish.
Upvotes: -1
Reputation: 6466
well the whole thing could be done with data binding so as you update the location of a bulb it auto updates the UI, stay with me here the code example is a bit long..
the result of this demo is just some circles with lines joining them but you could template this up however you want.
The xaml window, this declares an items control that will display the bulbs (and the lines joining them)
<Window x:Class="points.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:points"
DataContext="{Binding RelativeSource={RelativeSource Self}}"
Title="MainWindow" Height="350" Width="525">
<ItemsControl ItemsSource="{Binding Bulbs}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Canvas HorizontalAlignment="Left" VerticalAlignment="Top">
<Ellipse Fill="Blue" Width="10" Height="10">
<Ellipse.RenderTransform>
<TranslateTransform X="{Binding X}" Y="{Binding Y}"/>
</Ellipse.RenderTransform>
</Ellipse>
<Line X1="{Binding X}" Y1="{Binding Y}" X2="{Binding NextBulb.X}" Y2="{Binding NextBulb.Y}" Stroke="Red">
<Line.RenderTransform>
<TranslateTransform X="5" Y="5"/>
</Line.RenderTransform>
</Line>
</Canvas>
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</Window>
and this is the cs, I've created a bulb structure that holds the X and Y location of a bulb and a reference to the next bulb in the chain, I've placed all bulbs in to a line and then just moved the Y location of element 5 a bit lower to show it updating.
using System.Collections.ObjectModel;
using System.Windows;
namespace points
{
public class Bulb : DependencyObject
{
public double X
{
get { return (double)GetValue(XProperty); }
set { SetValue(XProperty, value); }
}
public static readonly DependencyProperty XProperty =
DependencyProperty.Register("X", typeof(double), typeof(Bulb), new PropertyMetadata(0d));
public double Y
{
get { return (double)GetValue(YProperty); }
set { SetValue(YProperty, value); }
}
public static readonly DependencyProperty YProperty =
DependencyProperty.Register("Y", typeof(double), typeof(Bulb), new PropertyMetadata(0d));
public Bulb NextBulb
{
get { return (Bulb)GetValue(NextBulbProperty); }
set { SetValue(NextBulbProperty, value); }
}
public static readonly DependencyProperty NextBulbProperty =
DependencyProperty.Register("NextBulb", typeof(Bulb), typeof(Bulb), new PropertyMetadata(null));
}
public partial class MainWindow : Window
{
public ObservableCollection<Bulb> Bulbs
{
get { return (ObservableCollection<Bulb>)GetValue(BulbsProperty); }
set { SetValue(BulbsProperty, value); }
}
public static readonly DependencyProperty BulbsProperty =
DependencyProperty.Register("Bulbs", typeof(ObservableCollection<Bulb>), typeof(MainWindow), new PropertyMetadata(null));
public MainWindow()
{
Bulbs = new ObservableCollection<Bulb>();
InitializeComponent();
for (int i = 0; i < 10; i++)
{
double x = i * 50;
double y = 25;
Bulbs.Add(new Bulb()
{
X = x,
Y = y,
NextBulb = i > 0 ? Bulbs[i - 1] : null,
});
}
Bulbs[5].Y = 50;
}
}
}
The outcome of this is that altering the Bulbs structure in any way will update the bulb display on the UI without having to clear and recreate your collection every time, you can even run animations on the properties for some pretty cool effects.
Upvotes: 3