Wednesday, April 26, 2017

WPF Tip #9 - ObservableCollection and the CollectionChanged Event

Tip #9 will examine something that seems like it should work according to the documentation on MSDN, but I have seen it not work as expected.

When using ObservableCollection<T>, if some code in your application needs to react when items are added or removed from the collection the CollectionChanged event can be handled.

private ObservableCollection<Pill> _pills = new ObservableCollection<Pill>();

public MainWindow()
{
    InitializeComponent();
    Pills.CollectionChanged += Pills_CollectionChanged;
}

public ObservableCollection<Pill> Pills
{
    get { return _pills; }
    set { _pills = value; }
}

void Pills_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
    // Pills Collection Changed - do something
}

The issue I have encountered with this code is that setting that value of the Pills property directly does not always fire the CollectionChanged event.

Pills = someOtherPillCollection;

What I did to work around this was to remove the property setter and only work with the collection through its properties. In this case, by using Clear() and Concat<T>().

Pills.Clear();
Pills.Concat<Pill>(someOtherPillCollection);

ObservableCollection does not have an AddRange() method. You could write your own extension method to do it, but I prefer to use the LINQ extension method Concat<T>().

Has anyone else seen this same issue?

 

del.icio.us Tags: ,

2 comments:

  1. Is this not as simple as: when you call the Pills setter, you are replacing the _pills ObservableCollection with a new object, and you do not have an event wired up to the CollectionChanged event on the new object?

    I think the workaround you use is actually the best way to use an ObservableCollection anyway - although it would be nice if it had an AddRange method, as you say.

    ReplyDelete
    Replies
    1. The CollectionChanged events were still firing on the Pills after replacing the collection, just not on the actual replacement. It would probably also work to leverage INotifyPropertyChange on the property set.

      Delete