CollectionChanged and IList of Items - why the difficulties
Asked Answered
P

4

13

I am looking into the topic why a ObservableCollection/ListCollectionView/CollectionView raises a NotSuportedException when calling the CollectionChanged with the parameter of IList.

//Throws an exception
private void collectionChanged_Removed(IList items)
{
    if (CollectionChanged != null)
        CollectionChanged(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, items));
}

I have found several Webpages, talking about this topic and they suggest either using the Reset ability to force a complete redraw of the UI, or just simply call for each item the CollectionChanged or some more creative way: http://geekswithblogs.net/NewThingsILearned/archive/2008/01/16/listcollectionviewcollectionview-doesnt-support-notifycollectionchanged-with-multiple-items.aspx

I just cant find the WHY? For me it makes no sense why this would be the case.

Is there any chance that this lacking feature, which we all face at some point in our Development Cycle, since the Add method just has to much of an overhead when you want to Add multiple items fast, will be implemented any time (.Net 5, C# 6...).

Edit:

In my specific case, I have written my own class :

public class ObservableList<T> : IList<T>, IList, IEnumerable<T>,
    INotifyCollectionChanged
{
    public event NotifyCollectionChangedEventHandler CollectionChanged;
    //other stuff...
}

And still throws the said NotSupportedException.

Pice answered 4/2, 2014 at 16:4 Comment(6)
connect.microsoft.com/visualstudio/feedback/details/515465/… : Closed as "Won't fix". Also has low votes.Crosscurrent
@Crosscurrent The fact that ObservableCollection doesn't have an AddRange isn't really the issue, because it's easy enough to write a custom class that does. The problem is the handler built in to CollectionView won't accept it. Specifically, it rejects any NotifyCollectionChangedEventArgs where the OldItems or NewItems list has multiple items.Mesoglea
Could you post the stacktrace?Indelicate
@Rand Random - I realized that I was confusing your ObservableList with the built-in ObservableCollection in my answer so I took that part of it out. Can you maybe include a reproducible scenario that demonstrates the exact issue?Costate
@Rand Random - Nevermind. VirtualBlackFox cleared things up regarding the nature of problem.Costate
@Rand Random: Have you tried using BulkObservableCollection<T> Class instead? That class offers an AddRange without firing an event for each item. Does it make a difference?Touchhole
C
4

Inspired by VirtualBlackFox's answer I took a look under the hood of the CollectionView classes in ILSpy. It appears that the primary reason why the lack of support for Range operations is because internally the CollectionView uses a change log to centrally manage pending changes of all kinds and dispatch messages on a per/item basis.

By its very purpose, the CollectionView could store 1000s of records used simultaneously with multiple UI controls representing its underlying data. So, adding or deleting records must be done on an atomic basis to maintain the integrity of the UI controls that access the view information. You can't synchronize incremental changes with multiple UI subscribers using bulk change events without passing the grouping, sorting, and filtering functionality of the CollectionView onto the UI controls that use it.

The CollectionView also derives from System.Windows.Threading.Dispatcher so the issue may be co-related to how it manages work items on it's thread. The call path includes a protected ProcessCollectionChanged method that specifically processes individual changes on the UI thread. So, updating ranges may interfere with the whole threading model it uses to interact with UI elements that use it.

I totally agree that having consumers of the CollectionView pass in an IList to NotifyCollectionChangedEventArgs is silly. It specifically rejects anything with a length != 1 and hard-codes for args.NewItems[0] internally.

Costate answered 7/2, 2014 at 17:41 Comment(0)
P
2

As @nmclean said in the comments the problem isn't in the collection emitting CollectionChanged but on the receiving end.

If you look at the code of ListCollectionView (Using DotPeek for example or on new versions of visual studio you can access the reference source code) you will notice that each time the attached collection change it call a method ValidateCollectionChangedEventArgs that throw when there is more than one element changed

private void ValidateCollectionChangedEventArgs(NotifyCollectionChangedEventArgs e)
{
  switch (e.Action)
  {
    case NotifyCollectionChangedAction.Add:
      if (e.NewItems.Count == 1)
        break;
      else
        throw new NotSupportedException(System.Windows.SR.Get("RangeActionsNotSupported"));
...

The rest of the class and it's CollectionView base class are already big beasts (2710 and 2027 lines in the source published on the reference source code) so Microsoft might have wanted to avoid supporting a complex case that the collection that they recommend don't create anyway.

(The method handling collection change is already 141 lines in the reference source code and adding multi element support will make it grow even more or need a careful split and potentially break other things...)

I didn't find any suggestions linked to adding support for range events in Microsoft Connect but you should submit your own if it is important for you.

Popple answered 8/2, 2014 at 19:33 Comment(1)
+1 for explanation of problem. I thought it was related to ObservableCollection. Good answer.Costate
T
0

I guess this is mainly for performance reasons. I was also shocked when I saw that CollectionView also does not accept the -1 value for NewStartingIndex or OldStartingIndex. So basically, CollectionView always wants a mapping from items to their indices. However, it does not require this mapping to be exact (which is strange from my point of view), it is allowed that NewStartingIndex is smaller than the correct index (unless it is -1).

I think the root of the problem is that large parts within Microsoft still think that a list is the one and only way to implement a collection, which of course just is not true. Here, the creators of NotifyCollectionChangedEventArgs thought about alternatives (such as linked lists or hashing collections), but the UI guys did not. Or at least they did not want to support these collections as they appear rather rarely.

Toogood answered 10/2, 2014 at 17:16 Comment(0)
M
0

The temporary solution is useless. It only hides the problems. The solution could lie in making events where you provide an entire new list to the observers. That way Microsoft won't have to implement efficient range handlers for each type of observer.

Madancy answered 12/2, 2014 at 15:14 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.