0

I have ObservableCollection that contains class: MyClass.

    private ObservableCollection<MyClass> _myCollection;

    public ObservableCollection<MyClass> MyCollection
    {
        get { return _myCollection; }
        set
        {
            _myCollection= value;
        }
    }

I want to know in C# code, if the collection changed.

So I registered to event of the collection: CollectionChanged it works when I add / delete a record.

Here the registred:

   MyCollection.CollectionChanged += new System.Collections.Specialized.NotifyCollectionChangedEventHandler(MyCollection_CollectionChanged);

I want this event to work (or register another event) when something changes within the class.

At first I thought to register for PropertyChanged event of the class, then through it run the event CollectionChanged - it seems complicated and unnecessary.

I think that as the binding of wpf can recognize a change in the class and a change in the collection itself, then it is possible to do this through code.

Am I wrong? (More precise question is: Does anyone know a way to this?)

Hodaya Shalom
  • 4,327
  • 12
  • 57
  • 111

2 Answers2

4

As already stated by Nik, the PropertyChanged event is the right way to go. But I think you shouldn't invoke the CollectionChanged event manually or "force" it that way. You should just refactor your code inside the event, like this:

public class MyClass : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    private string _name;
    public string Name
    {
        get { return _name; }
        set { _name = value; OnPropertyChanged("Name"); }
    }

    private void OnPropertyChanged(string propertyName)
    {
        if (PropertyChanged != null)
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
    }
}

public class Class1
{
    private ObservableCollection<MyClass> _myCollection = new ObservableCollection<MyClass>();
    public ObservableCollection<MyClass> MyCollection
    {
        get { return _myCollection; }
        // set { _myCollection = value; } // see Clemens' comment
    }

    public Class1()
    {
        // hook up this event at the appropriate place, does not have to be the ctor
        MyCollection.CollectionChanged += MyCollection_CollectionChanged;

        MyClass m = new MyClass() { Name = "Example" };

        MyCollection.Add(m); // calls ExecuteOnAnyChangeOfCollection
        m.Name = "ChangedValue"; // calls ExecuteOnAnyChangeOfCollection
    }

    void MyCollection_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
    {
        if (e.NewItems != null)
        {
            foreach (MyClass item in e.NewItems)
                item.PropertyChanged += MyClass_PropertyChanged;
        }

        if (e.OldItems != null)
        {
            foreach (MyClass item in e.OldItems)
                item.PropertyChanged -= MyClass_PropertyChanged;
        }

        ExecuteOnAnyChangeOfCollection();
    }

    void MyClass_PropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        ExecuteOnAnyChangeOfCollection();
    }

    private void ExecuteOnAnyChangeOfCollection()
    {
        // handling code ...
        System.Windows.MessageBox.Show("Collection has changed.");
    }
Mike Fuchs
  • 12,081
  • 6
  • 58
  • 71
  • 1
    +1, but you would have to add/remove the `MyCollection_CollectionChanged` handler in the `MyCollection` property setter. – Clemens Mar 10 '13 at 15:58
  • @Clemens: I can see what you're saying, and not doing so might lead to ugly problems that are hard to detect (MyCollection set from outside the class, event no longer firing), but don't you think that depends on the use case? For example, the initial data load might not be meant to trigger `ExecuteOnAnyChangeOfCollection`. I would most probably make the property readonly - I'll change it to that. Thanks for hint (+1), even if the discussion is outside the scope of the question. – Mike Fuchs Mar 10 '13 at 16:23
  • You should invoke a CollectionChanged event somehow on item's property changes, if you want those changes to be displayed on the UI. And normally you do – Nikita B Mar 11 '13 at 05:28
  • @adabyron, So, it's like I wanted to do from the beginning, there is no way short. Thanks anyway. – Hodaya Shalom Mar 11 '13 at 06:10
  • @Nik: Property changes will automatically be displayed in the UI because of the PropertyChanged event (INotifyPropertyChanged), you do not need to invoke the CollectionChanged event to achieve that. – Mike Fuchs Mar 11 '13 at 07:20
  • @adabyron: It will not. Check this out: http://stackoverflow.com/questions/1427471/observablecollection-not-noticing-when-item-in-it-changes-even-with-inotifyprop – Nikita B Mar 11 '13 at 12:12
  • @Nik: :)... Use my Class1 as a viewmodel. Bind MyCollection as the itemssource to e.g. a DataGrid. What do you think will show up as the Name, "Example" or "ChangedValue"? The post you linked discusses that the CollectionChanged event is not raised when a item property changes. That is correct - but does not mean that the UI will not notice, who listens also to the PropertyChanged event. – Mike Fuchs Mar 11 '13 at 12:49
2

You are right. The easiest way to get notifications from MyClass is to subscribe to PropertyChanged event (if MyClass implements INotifyPropertyChanged).

Then to update UI you can either Remove and then Add the changed item (this will rise CollectionChanged automatically) or invoke CollectionChanged event with Reset parameter manually (you will need to extend ObservableCollection class to do so, but thats a fairly easy task).

Nikita B
  • 3,303
  • 1
  • 23
  • 41