Reputation: 1819
I have the following viewmodel which I'd like to bind to a view:
public class EntityManagerModel
{
public readonly ObservableCollection<EntityViewModel> m_Entities =
new ObservableCollection<EntityViewModel>();
}
//
// implements INotifyPropertyChanged
public class EntityManagerViewModel: BaseViewModel
{
private EntityManagerModel m_Model = new EntityManagerModel();
public ObservableCollection<EntityViewModel> Entities
{
get { return m_Model.m_Entities; }
}
}
However what I actually would like to bind to my view is the data contained in EntityViewModel
which is defined like so:
public class EntityModel
{
public readonly PointCollection m_TrailPoints = new PointCollection();
}
//
// implements INotifyPropertyChanged
public class EntityViewModel: BaseViewModel
{
private EntityModel m_Model = new EntityModel;
public PointCollection TrailPoints
{
get { return m_Model.m_TrailPoints; }
}
}
So, I'd like my view to update every time I add a point like this:
// In some routine inside EntityManageViewModel I have this code
// this should trigger collectionChanged event:
Entities[index].TrailPoints.Add( new Point(x, y));
My view's XAML code is:
<!-- EntityManagerView -->
<Grid>
<!--Trail line-->
<ItemsControl ItemsSource="{Binding Entities}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate DataType="{x:Type vm:EntityViewModel}">
<Polyline Stroke="Red" StrokeThickness="1">
<Polyline.Style>
<Style TargetType="{x:Type Polyline}">
<Setter Property="Points">
<Setter.Value>
<MultiBinding Converter="{StaticResource pointMultiConverter}">
<Binding Path="ActualWidth" RelativeSource="{RelativeSource FindAncestor, AncestorType={x:Type Canvas}}"/>
<Binding Path="ActualHeight" RelativeSource="{RelativeSource FindAncestor, AncestorType={x:Type Canvas}}"/>
<Binding Path="TrailPoints"/>
</MultiBinding>
</Setter.Value>
</Setter>
</Style>
</Polyline.Style>
</Polyline>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
This only displays/updates the lines when i resize the application window. Of course, I have found solutions like this while searching. But subclassing ObservableCollection
introduces other problems in my code. So, my question is what is the best/alternative way to do this?
Upvotes: 0
Views: 520
Reputation: 4298
The easiest solution to "simulate" an observable collection is to add the following line to the constructor of your EntityViewModel
:
public EntityViewModel()
{
TrailPoints.Changed += (sender, args) => RaisePropertyChanged("TrailPoints");
}
Whenever the TrailPoints
collection is changed (e.g. when a Point
is added), its Changed
event is raised. The idea is to subscribe to this event and raise the PropertyChanged
event to inform the View about the collection has changed!
Upvotes: 1
Reputation: 10624
Basically, you need to hook into the collection changed on your parent viewmodel.
this.Entities.CollectionChanged += new System.Collections.Specialized.NotifyCollectionChangedEventHandler(EntitiesItems_CollectionChanged);
void EntitiesItems_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e){
if (e.NewItems != null) {
((EntityModel)e.NewItems[0]).PropertyChanged += new System.ComponentModel.PropertyChangedEventHandler(TrailPoints_PropertyChanged);
}
OnPropertyChanged("Entities");
}
void TrailPoints_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e) {
if (e.PropertyName == "TrailPoints") {
OnPropertyChanged("Entities");
}
}
The INotifyPropertyChanged on your current viewModel only goes one level deep. By hooking into every property changed in the collection, you can determine what to do with specific changes to the collection (added, deleted, etc.)
Upvotes: 0