please note that I am trying to use NotifyCollectionChangedAction.Add action instead of .Reset. the latter does work, but it is not very efficient with large collections.
so i subclassed ObservableCollection:
public class SuspendableObservableCollection<T> : ObservableCollection<T>
for some reason, this code:
private List<T> _cachedItems;
...
public void FlushCache() {
if (_cachedItems.Count > 0) {
foreach (var item in _cachedItems)
Items.Add(item);
OnCollectionChanged(new NotifyCollectionChangedEventArgs(
NotifyCollectionChangedAction.Add, (IList<T>)_cachedItems));
}
}
is throwing A collection Add event refers to item that does not belong to collection
this appears to be a bug in BCL ?
I can step through and see prior to calling OnCollectionChanged that new items are added to this.Items
WOW
just made a staggering discovery. None of these approaches worked for me (flush, addrange), because the error appears to be triggered ONLY if this collection is bound to my Listview!!
TestObservableCollection<Trade> testCollection = new TestObservableCollection<Trade>();
List<Trade> testTrades = new List<Trade>();
for (int i = 0; i < 200000; i++)
testTrades.Add(t);
testCollection.AddRange(testTrades); // no problems here..
_trades.AddRange(testTrades); // this one is bound to ListView .. BOOOM!!!
In conclusion, ObservableCollection does support adding incremental lists, but a ListView doesn't. Andyp figured out a workaround to make it work with CollectionView below, but since .Refresh() is called, that is no different than just calling OnCollectionChanged( .Reset )..
you can implement AddRange() for the ObservableCollection like this as shown here:
public class RangeObservableCollection<T> : ObservableCollection<T>
{
private bool _SuppressNotification;
public override event NotifyCollectionChangedEventHandler CollectionChanged;
protected virtual void OnCollectionChangedMultiItem(
NotifyCollectionChangedEventArgs e)
{
NotifyCollectionChangedEventHandler handlers = this.CollectionChanged;
if (handlers != null)
{
foreach (NotifyCollectionChangedEventHandler handler in
handlers.GetInvocationList())
{
if (handler.Target is CollectionView)
((CollectionView)handler.Target).Refresh();
else
handler(this, e);
}
}
}
protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
{
if (!_SuppressNotification)
{
base.OnCollectionChanged(e);
if (CollectionChanged != null)
CollectionChanged.Invoke(this, e);
}
}
public void AddRange(IEnumerable<T> list)
{
if (list == null)
throw new ArgumentNullException("list");
_SuppressNotification = true;
foreach (T item in list)
{
Add(item);
}
_SuppressNotification = false;
OnCollectionChangedMultiItem(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, list));
}
}
UPDATE: After binding to ListBox I was seeing an InvalidOperationException too (same message you were seeing). According to this article that's because CollectionView doesn't support range actions. Luckily the article also supplies a solution (although it feels a little "hack-ish").
UPDATE 2: Added a fix that raises the overridden CollectionChanged event in the overridden implementation of OnCollectionChanged().