Reputation: 1546
I am trying to draw a polyline on a canvas which will have rectangle on each point. The Polyline is bound to a collection of points from the ViewModel.
When I try to set DataTemplate
for each point (like below) it shows no rectangle on polyline points.
Is there some way to display rectangle on polyline points?
Later I want to adjust the polyline by dragging these points.
<Polyline Points="{Binding EdgePoints, Converter={StaticResource pointCollectionConverter}}" StrokeThickness="2">
<Polyline.Resources>
<DataTemplate DataType="{x:Type Point}">
<Rectangle Width="20" Height="20" Fill="Black"/>
</DataTemplate>
</Polyline.Resources>
</Polyline>
Here is example where I want to draw rectangles.
Upvotes: 0
Views: 2088
Reputation: 128013
You could have a view model like the one shown below. Besides the obvious parts, it attaches/detaches a PropertyChanged handler to/from each Vertex
in order to fire the PropertyChanged event for the Vertices
property. This is necessary to update the Polyline's Point Binding.
public class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
public class Vertex : ViewModelBase
{
private Point point;
public Point Point
{
get { return point; }
set { point = value; OnPropertyChanged(); }
}
}
public class ViewModel : ViewModelBase
{
public ViewModel()
{
Vertices.CollectionChanged += VerticesCollectionChanged;
}
public ObservableCollection<Vertex> Vertices { get; }
= new ObservableCollection<Vertex>();
private void VerticesCollectionChanged(
object sender, NotifyCollectionChangedEventArgs e)
{
if (e.Action == NotifyCollectionChangedAction.Add)
{
foreach (var item in e.NewItems.OfType<INotifyPropertyChanged>())
{
item.PropertyChanged += VertexPropertyChanged;
}
}
else if (e.Action == NotifyCollectionChangedAction.Remove)
{
foreach (var item in e.OldItems.OfType<INotifyPropertyChanged>())
{
item.PropertyChanged -= VertexPropertyChanged;
}
}
OnPropertyChanged(nameof(Vertices));
}
private void VertexPropertyChanged(object sender, PropertyChangedEventArgs e)
{
OnPropertyChanged(nameof(Vertices));
}
}
The Vertices to PointCollection converter could look like this:
public class VerticesConverter : IValueConverter
{
public object Convert(
object value, Type targetType, object parameter, CultureInfo culture)
{
var vertices = value as IEnumerable<Vertex>;
return vertices != null
? new PointCollection(vertices.Select(v => v.Point))
: null;
}
public object ConvertBack(
object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotSupportedException();
}
}
The view would use a Polyline and an ItemsControl. The ItemsTemplate would declare a Thumb
element that handles the dragging of vertex points.
<Canvas>
<Canvas.Resources>
<local:VerticesConverter x:Key="VerticesConverter"/>
<Style x:Key="ThumbStyle" TargetType="Thumb">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Thumb">
<Rectangle Fill="Transparent" Stroke="Red"
Width="10" Height="10" Margin="-5,-5"/>
</ControlTemplate>
</Setter.Value>
</Setter>
<EventSetter Event="DragDelta" Handler="ThumbDragDelta"/>
</Style>
</Canvas.Resources>
<Polyline Points="{Binding Vertices, Converter={StaticResource VerticesConverter}}"
Stroke="DarkBlue" StrokeThickness="3" StrokeLineJoin="Round"/>
<ItemsControl ItemsSource="{Binding Vertices}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemContainerStyle>
<Style TargetType="ContentPresenter">
<Setter Property="Canvas.Left" Value="{Binding Point.X}"/>
<Setter Property="Canvas.Top" Value="{Binding Point.Y}"/>
</Style>
</ItemsControl.ItemContainerStyle>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Thumb Style="{StaticResource ThumbStyle}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Canvas>
Finally, the Thumb's DragDelta handler:
private void ThumbDragDelta(object sender, DragDeltaEventArgs e)
{
var vertex = (Vertex)((Thumb)sender).DataContext;
vertex.Point = new Point(
vertex.Point.X + e.HorizontalChange,
vertex.Point.Y + e.VerticalChange);
}
Upvotes: 3