When Clearing an ObservableCollection, There are No Items in e.OldItems When Clearing an ObservableCollection, There are No Items in e.OldItems wpf wpf

When Clearing an ObservableCollection, There are No Items in e.OldItems


It doesn't claim to include the old items, because Reset doesn't mean that the list has been cleared

It means that some dramatic thing has taken place, and the cost of working out the add/removes would most likely exceed the cost of just re-scanning the list from scratch... so that's what you should do.

MSDN suggests an example of the entire collection being re-sorted as a candidate for reset.

To reiterate. Reset doesn't mean clear, it means Your assumptions about the list are now invalid. Treat it as if it's an entirely new list. Clear happens to be one instance of this, but there could well be others.

Some examples:
I've had a list like this with a lot of items in it, and it has been databound to a WPF ListView to display on-screen.
If you clear the list and raise the .Reset event, the performance is pretty much instant, but if you instead raise many individual .Remove events, the performance is terrible, as WPF removes the items one by one. I've also used .Reset in my own code to indicate that the list has been re-sorted, rather than issuing thousands of individual Move operations. As with Clear, there is a large performance hit when when raising many individual events.


We had the same issue here. The Reset action in CollectionChanged does not include the OldItems. We had a workaround: we used instead the following extension method:

public static void RemoveAll(this IList list){   while (list.Count > 0)   {      list.RemoveAt(list.Count - 1);   }}

We ended up not supporting the Clear() function, and throwing a NotSupportedException in CollectionChanged event for Reset actions. The RemoveAll will trigger a Remove action in CollectionChanged event, with the proper OldItems.


Another option is to replace the Reset event with a single Remove event that has all the cleared items in its OldItems property as follows:

public class ObservableCollectionNoReset<T> : ObservableCollection<T>{    protected override void ClearItems()    {        List<T> removed = new List<T>(this);        base.ClearItems();        base.OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, removed));    }    protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)    {        if (e.Action != NotifyCollectionChangedAction.Reset)            base.OnCollectionChanged(e);    }    // Constructors omitted    ...}

Advantages:

  1. No need to subscribe to an additional event (as required by accepted answer)

  2. Doesn't generate an event for each object removed (some other proposed solutions result in multiple Removed events).

  3. Subscriber only needs to check NewItems & OldItems on any event to add/remove event handlers as required.

Disadvantages:

  1. No Reset event

  2. Small (?) overhead creating copy of list.

  3. ???

EDIT 2012-02-23

Unfortunately, when bound to WPF list based controls, Clearing a ObservableCollectionNoReset collection with multiple elements will result in an exception "Range actions not supported".To be used with controls with this limitation, I changed the ObservableCollectionNoReset class to:

public class ObservableCollectionNoReset<T> : ObservableCollection<T>{    // Some CollectionChanged listeners don't support range actions.    public Boolean RangeActionsSupported { get; set; }    protected override void ClearItems()    {        if (RangeActionsSupported)        {            List<T> removed = new List<T>(this);            base.ClearItems();            base.OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, removed));        }        else        {            while (Count > 0 )                base.RemoveAt(Count - 1);        }                    }    protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)    {        if (e.Action != NotifyCollectionChangedAction.Reset)            base.OnCollectionChanged(e);    }    public ObservableCollectionNoReset(Boolean rangeActionsSupported = false)     {        RangeActionsSupported = rangeActionsSupported;    }    // Additional constructors omitted. }

This isn't as efficient when RangeActionsSupported is false (the default) because one Remove notification is generated per object in the collection