0

Suppose, I have the following class hierarchy:

public NestedClass : INotifyPropertyChanged {
    string prop1;

    public String  Prop1 {
        get { return prop1; }
        set {
            prop1 = value;
            OnPropertyChanged("Prop1");
        }
    }

    void OnPropertyChanged(String name) {
        PropertyChangedEventHandler handler = PropertyChanged;
        if (handler != null) {
            handler(this, new PropertyChangedEventArgs(name));
        }
    }
    public event PropertyChangedEventHandler PropertyChanged;
}

public Class1 : INotifyPropertyChanged {
    ObservableCollection<NestedClass> collection;

    public Class1() {
        collection = new ObservableCollection<NestedClass>();
    }

    public ObservableCollection<NestedClass>  Collection {
        get { return collection; }
        set {
            if (collection != null) {
                collection.CollectionChanged -= childOnCollectionChanged;
            }
            collection = value;
            if (collection != null) {
                collection.CollectionChanged += childOnCollectionChanged;
            }
        }
    }

    void childOnCollectionChanged(Object sender, NotifyCollectionChangedEventArgs e) {
        switch (e.Action) {
            case NotifyCollectionChangedAction.Add:
                ((INotifyPropertyChanged)e.NewItems[0]).PropertyChanged += childOnPropertyChanged;
                break;
            case NotifyCollectionChangedAction.Remove:
                ((INotifyPropertyChanged)e.OldItems[0]).PropertyChanged -= childOnPropertyChanged;
                break;
            case NotifyCollectionChangedAction.Reset:
                if (e.OldItems == null) { break; }
                foreach (INotifyPropertyChanged itemToRemove in e.OldItems) {
                    itemToRemove.PropertyChanged -= childOnPropertyChanged;
                }
                break;
        }
    }
    void childOnPropertyChanged(Object sender, PropertyChangedEventArgs e) {
        OnPropertyChanged("nested");
    }

    void OnPropertyChanged(String name) {
        PropertyChangedEventHandler handler = PropertyChanged;
        if (handler != null) {
            handler(this, new PropertyChangedEventArgs(name));
        }
    }
    public event PropertyChangedEventHandler PropertyChanged;
}

there is class Class1 which contains some properties and several collections of different classes. All collections are defined in the same was as a collection of NestedClass. They are properly bound to the UI, I have no problems there.

Class1 object and nested objects/collections can be created at runtime, or can be generated by a XML deserializer. To properly handle deserialized collection, I have to subscribe to INotifyPropertyChanged event of each collection item when collection is assigned.

My goal is to handle all changes of all items of NestedClass in Class1 to get information that data was changed (and pass this information up to its parent).

The problem is that I have mutiple nested collections and they notify parent class Class1 and it successfully bubbles the event to its parent. However, one collection notifies parent Class1 about changes, but handler in the Class1 is null, as the result Class1 doesn't bubble event to its parent class (is not shown here as irrelevant). I went through debugger but was unable to find where is the problem. When other nested collections call parent's OnPropertyChanged, the handler is not null and everything is working correctly.

Edit: this issue is raised only when Class1 and NestedClass objects are generated by XML deserializer.

I read a lot of similar posts on SO, but most of them are about invalid DataContext in the view, which is not my case (I believe). What else I can check to find the root of the issue?

Crypt32
  • 12,850
  • 2
  • 41
  • 70
  • I doubt it will help you, but you might want to consider switching to a [`BindingList`](https://msdn.microsoft.com/en-us/library/ms132679(v=vs.110).aspx) instead of a `ObserveableCollection`, it has the logic you do in `childOnCollectionChanged` already built in to it. Just make sure `RaiseListChangedEvents` is set to true and the `ListChanged` event will be raised with `args.ListChangedType == ListChangedType.ItemChanged` any time one of the members raises its `PropertyChanged` event – Scott Chamberlain Jul 21 '16 at 17:07
  • Thanks for tip! I'll read it in details. But someone claims that `BindingList` is not properly supported in WPF: http://stackoverflow.com/a/4284904/3997611 – Crypt32 Jul 21 '16 at 17:19
  • BindingList is unnecessary (and so is your solution) for WPF bindings. When you do a `DataSource="{Binding Path=InstanceOfClass1.Collection}"` as a list or a tables data source, then on the row level you have `Text="{Binding Path=Prop1}"` the individual rows automatically connect to each row in the collection, you don't need to "help it" by bubbling up the binding. BindingLists work fine in WPF, they are not bugged, it is just they are not necessary and introduce overhead. – Scott Chamberlain Jul 21 '16 at 17:49
  • To downvoter: what is wrong with the question? – Crypt32 Jul 21 '16 at 17:53
  • 1
    A frindly hint which might also solve your issue: **Never** make your `ObservableCollection` public settable. If the whole collection is newly set, it breaks the binding in the view, because view's listener is still subscribed to the old collection. Use `.Clear()` instead, and add the items. This might give you struggles on serialisation, but its worth it. Also, have an upvote. Strange people are around these days – lokusking Jul 21 '16 at 18:27
  • `If the whole collection is newly set, it breaks the binding in the view, because view's listener is still subscribed to the old collection`, yes, I'm aware about this. And my setter handles subscriber management already. – Crypt32 Jul 21 '16 at 18:30
  • @ScottChamberlain can you post your response as answer? I just managed BindingList to work. Though, the `ListChanged` event is raised twice when I edit only one property. – Crypt32 Jul 21 '16 at 18:34

1 Answers1

0

You might want to consider switching to a BindingList<T> instead of a ObserveableCollection<T>, it has the logic you do in childOnCollectionChanged already built in to it. Just make sure RaiseListChangedEvents is set to true and the ListChanged event will be raised with args.ListChangedType == ListChangedType.ItemChanged any time one of the members raises its PropertyChanged event.

Scott Chamberlain
  • 124,994
  • 33
  • 282
  • 431
  • Although `BindingList` adds extra overhead (acceptable for me since the collection contains only dozen items), it is exactly what I need as I need to listen to collection item changes as well (not only collection changes). – Crypt32 Jul 21 '16 at 18:45