I have an ObservableCollection
of items that is bound to a list control in my view.
I have a situation where I need to add a chunk of values to the start of the collection. Collection<T>.Insert
documentation specifies each insert as an O(n) operation, and each insert also generates a CollectionChanged
notification.
Therefore I would ideally like to insert the whole range of items in one move, meaning only one shuffle of the underlying list, and hopefully one CollectionChanged
notification (presumably a "reset").
Collection<T>
does not expose any method for doing this. List<T>
has InsertRange()
, but IList<T>
, that Collection<T>
exposes via its Items
property does not.
Is there any way at all to do this?
WPF ObservableCollection An ObservableCollection is a dynamic collection of objects of a given type. Objects can be added, removed or be updated with an automatic notification of actions. When an object is added to or removed from an observable collection, the UI is automatically updated.
The ObservableCollection exposes an protected Items
property which is the underlying collection without the notification semantics. This means you can build a collection that does what you want by inheriting ObservableCollection:
class RangeEnabledObservableCollection<T> : ObservableCollection<T> { public void InsertRange(IEnumerable<T> items) { this.CheckReentrancy(); foreach(var item in items) this.Items.Add(item); this.OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)); } }
Usage:
void Main() { var collection = new RangeEnabledObservableCollection<int>(); collection.CollectionChanged += (s,e) => Console.WriteLine("Collection changed"); collection.InsertRange(Enumerable.Range(0,100)); Console.WriteLine("Collection contains {0} items.", collection.Count); }
To make the above answer useful w/o deriving a new base class using reflection, here's an example:
public static void InsertRange<T>(this ObservableCollection<T> collection, IEnumerable<T> items) { var enumerable = items as List<T> ?? items.ToList(); if (collection == null || items == null || !enumerable.Any()) { return; } Type type = collection.GetType(); type.InvokeMember("CheckReentrancy", BindingFlags.Instance | BindingFlags.InvokeMethod | BindingFlags.NonPublic, null, collection, null); var itemsProp = type.BaseType.GetProperty("Items", BindingFlags.NonPublic | BindingFlags.FlattenHierarchy | BindingFlags.Instance); var privateItems = itemsProp.GetValue(collection) as IList<T>; foreach (var item in enumerable) { privateItems.Add(item); } type.InvokeMember("OnPropertyChanged", BindingFlags.Instance | BindingFlags.InvokeMethod | BindingFlags.NonPublic, null, collection, new object[] { new PropertyChangedEventArgs("Count") }); type.InvokeMember("OnPropertyChanged", BindingFlags.Instance | BindingFlags.InvokeMethod | BindingFlags.NonPublic, null, collection, new object[] { new PropertyChangedEventArgs("Item[]") }); type.InvokeMember("OnCollectionChanged", BindingFlags.Instance | BindingFlags.InvokeMethod | BindingFlags.NonPublic, null, collection, new object[]{ new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)}); }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With