tags:

views:

402

answers:

3

I have a ViewModel class that contains a list of points, and I am trying to bind it to a Polyline. The Polyline picks up the initial list of points, but does not notice when additional points are added even though I implement INotifyPropertyChanged. What's wrong?

<StackPanel>
    <Button Click="Button_Click">Add!</Button>
    <Polyline x:Name="_line" Points="{Binding Pts}" Stroke="Black" StrokeThickness="5"/>
</StackPanel>

C# side:

// code-behind
_line.DataContext = new ViewModel();
private void Button_Click(object sender, RoutedEventArgs e)
{
 // The problem is here: NOTHING HAPPENS ON-SCREEN!
 ((ViewModel)_line.DataContext).AddPoint();
}

// ViewModel class
public class ViewModel : INotifyPropertyChanged
{
 public event PropertyChangedEventHandler PropertyChanged;

 public PointCollection Pts { get; set; }

 public ViewModel()
 {
  Pts = new PointCollection();
  Pts.Add(new Point(1, 1));
  Pts.Add(new Point(11, 11));
 }

 public void AddPoint()
 {
  Pts.Add(new Point(25, 13));
  if (PropertyChanged != null)
   PropertyChanged(this, new PropertyChangedEventArgs("Pts"));
 }
}
+3  A: 

It is quite likely that since it is binding to the collection, it will need something like ObservableCollection<T>. What happens if you switch from PointCollection to ObservableCollection<Point>?

Marc Gravell
I just tested the same scenario, the binding itself works (the Points property contains the new points)... but for some reason, the PolyLine isn't updated
Thomas Levesque
Very interesting, Thomas.
Qwertie
ObservableCollection<Point> does not work. Even the initial line does not appear on the screen; apparently Polyline doesn't support binding to ObservableCollection<Point>, nor does it support List<Point>.
Qwertie
+1  A: 

Change your PointCollections Property to a dependency property:

public PointCollection Pts
     {
      get { return (PointCollection)GetValue(PtsProperty); }
      set { SetValue(PtsProperty, value); }
     }

     // Using a DependencyProperty as the backing store for Pts.  This enables animation, styling, binding, etc...
     public static readonly DependencyProperty PtsProperty =
      DependencyProperty.Register("Pts", typeof(PointCollection), typeof(ViewModel), new UIPropertyMetadata(new PointCollection()));

BTW Doing this, you won't need to fire the PropertyChanged event.

Oh sorry, and your object needs to inherit from DependencyObject

    public class ViewModel : DependencyObject 
{ 
//... 
}
Carlo
It is usually not recommended to implement ViewModels as DependencyObjects, which are much heavier than POCO implementing INotifyPropertyChanged... anyway, it won't change anything in that case
Thomas Levesque
Thanks, did you test your answer? Using INotifyPropertyChanged works for me when I use it for other properties, just not for the point collection.
Qwertie
Yeah tested it, I changed the code when creating a point, instead of Pts.Add(new Point(25, 13)); I did Pts.Add(new Point(random.Next(0,100), random.Next(0,100))); and all the new random points were created and displayed on the screen.
Carlo
Thanks. I heard one shouldn't use both INotifyPropertyChanged and dependency properties in the same object; so potentially, I'd have to reprogram the rest of the ViewModel to use DPs. However, I just did a simple test that suggests using INotifyPropertyChanged for some properties and DPs for others DOES work.
Qwertie
A: 

i got it to work as a POCO by implementing INotifyCollectionChanged instead of INotifyPropertyChanged.

skj