简体   繁体   中英

ObservableCollection<T> isn't getting notified of change in property of <T>

I have a an ObservableCollection of Component, a class with another ObservableCollection, a String and a ComponentVersion. The SelectedComponentVersion is being updated via my view correctly but I'm unable to get the property of Components to fire it's setter/OnPropertyChanged.

private ObservableCollection<Component> components
public ObservableCollection<Component> Components
{
   get { return foo; }
   set { foo = value; OnPropertyChanged(); }
}

Here is the class of Component.

public class Component : ViewModelBase
{
  private string componentName;
  private ObservableCollection<ComponentVersion> componentVersions;
  private ComponentVersion selectedComponent;

  public string ComponentName
  {
     get { return componentName; }
     set { componentName = value; OnPropertyChanged(); }
  }
  public ObservableCollection<ComponentVersion> ComponentVersions
  {
     get { return componentVersions; }
     set { componentVersions = value; OnPropertyChanged(); }
  }
  public ComponentVersion SelectedComponent
  {
     get { return selectedComponent; }
     set { selectedComponent = value; OnPropertyChanged(); }
  }

  public Component(string componentName, List<ComponentVersion> componentVersion)
  {
     ComponentName = componentName;
     ComponentVersions = componentVersion.ToObservableCollection();
  }
}

I then have a binding from a listview onto the SelectedComponent property inside of Component.

I have read countless stack overflows about setting up CollectionChanged and have tried to implement it with no luck.

Components.CollectionChanged += stuff;
private void stuff(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
   throw new System.NotImplementedException();
}

but this is never hit as tested with breakpoints.

Am I missing something entirely, losing my mind or daft! Someone please give me a point in the right direction, if any of this makes any sense at all.

PS another solution I though of would be to place an invisible button inside the listview and have that send a command to tell the vm that a selected item has been updated.

In cases like this where I want to do something when the property on an item inside the collection changes , I usually hook up a PropertyChanged event in the CollectionChanged event

Here's a code example :

public MyViewModel()
{
    // Setup Collection, with a CollectionChanged event
    Components = new ObservableCollection<Component>();
    Components.CollectionChanged += Components_CollectionChanged;

}

// In the CollectionChanged event (items getting added or removed from collection),
// hook up the PropertyChanged event
void Components_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
    if (e.NewItems != null)
        foreach(MyType item in e.NewItems)
            item.PropertyChanged += Component_PropertyChanged;

    if (e.OldItems != null)
        foreach(MyType item in e.OldItems)
            item.PropertyChanged -= Component_PropertyChanged;
}

// In the PropertyChanged event, run some code if SelectedComponent property changed
void Component_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
    if (e.PropertyName == "SelectedComponent")
        DoWork();
} 

Note that we are using two separate events here.

  • CollectionChanged event runs when the collection itself changes. This means it gets set to a new collection, or item(s) get added or removed from the collection.

    This captures newly added items and hooks up the PropertyChanged handler, or detaches the PropertyChanged handler for items being removed from the collection.

  • PropertyChanged event runs when a property changes and fires the event. You'll use this event to run code when the SelectedComponent property on any item in the collection changes

Well, components hasn't changed. I'm not sure if you set up your handler correctly, but even if you did, a CollectionChanged event is only fired, if the collection changed (item added or removed).

Lets say you have a collection of cars and all are red.

You pick one car and set it's color to blue.

The collection has not changed. It's still the very same cars. No car is missing, no car was added.

You probably want to attach yourself to the handlers of all cars instead of the handler of the collection.

So to sum it up:

ObservableCollection<T> isn't getting notified of change in property of <T>

That's true and it's by design.

When adding items to the collection you want to "hook up" the events you are interested in for those items, then you can act as needed. As mentioned above, the collection isn't changing it is the individual items that are changing.

One thing you could do is extend the ObservableCollection class and override the functionality so that when the collection is changed, an item is either added or removed, you then go through the items in the collection and "hook up" the events you're interested in. One thing to note is you may have to go through the collection and remove the event handlers and "hook up" them again in order to stop getting multiple event handlers being set for the items in the list.

ObservableCollection<T> does not send notifications about property changes of the elements. However, the System.ComponentModel namespace contains another collection, which supports that: you can consider to use BindingList<T> instead, which is also supported by WPF (elements must implement INotifyPropertyChanged ).

Please note though that BindingList<T> scales poorly and its performance starts to decline above hundreds and thousands of elements as it always searches for the changed element sequentially in order to return the element index in its ListChanged event.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM