请注意,我正在尝试使用NotifyCollectionChangedAction.Add操作而不是.Reset。后者确实有效,但是对于大型的集合,它并不是很有效。
因此,我对ObservableCollection进行了子类:
public class SuspendableObservableCollection<T> : ObservableCollection<T>
出于某种原因,此代码:
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));
}
}
正在抛出集合添加事件引用不属于集合的项
这似乎是BCL中的一个bug?
在调用OnCollectionChanged之前,我可以逐步了解并看到新项被添加到this.Items中
WOW
刚刚有了一个惊人的发现。这些方法都不适用于我(刷新、加载项),因为只有当这个集合绑定到我的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!!!
总之,ObservableCollection确实支持添加增量列表,但ListView不支持。Andyp想出了一个解决办法,使它在下面的CollectionView中工作,但是由于调用了.Refresh(),这与调用OnCollectionChanged( .Reset )没有什么不同。
发布于 2010-07-21 19:16:17
可以为ObservableCollection实现如下所示的ObservableCollection()
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));
}
}
更新:绑定到ListBox之后,我也看到了一个InvalidOperationException (与您看到的消息相同)。根据这个文章,这是因为CollectionView不支持范围操作。幸运的是,这篇文章还提供了一个解决方案(尽管它感觉有点"hack-ish")。
UPDATE 2:添加了一个在重写的OnCollectionChanged()实现中引发重写的OnCollectionChanged事件的修复。
发布于 2012-02-23 15:51:59
谢谢你的灵感AndyP。在您的实现中,我遇到了一些问题,比如在测试中使用CollectionView而不是ICollectionView,以及手动调用元素的“重置”。继承自CollectionView的元素实际上可能以更多的方式处理这些args,而不是调用"this.Reset()",因此最好还是继续激发它们的处理程序,只使用它们所需的Action=Reset args,而不是包含更改的项列表的改进的事件args。下面是我的(非常类似的)实现。
public class BaseObservableCollection<T> : ObservableCollection<T>
{
//Flag used to prevent OnCollectionChanged from firing during a bulk operation like Add(IEnumerable<T>) and Clear()
private bool _SuppressCollectionChanged = false;
/// Overridden so that we may manually call registered handlers and differentiate between those that do and don't require Action.Reset args.
public override event NotifyCollectionChangedEventHandler CollectionChanged;
public BaseObservableCollection() : base(){}
public BaseObservableCollection(IEnumerable<T> data) : base(data){}
#region Event Handlers
protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
{
if( !_SuppressCollectionChanged )
{
base.OnCollectionChanged(e);
if( CollectionChanged != null )
CollectionChanged.Invoke(this, e);
}
}
//CollectionViews raise an error when they are passed a NotifyCollectionChangedEventArgs that indicates more than
//one element has been added or removed. They prefer to receive a "Action=Reset" notification, but this is not suitable
//for applications in code, so we actually check the type we're notifying on and pass a customized event args.
protected virtual void OnCollectionChangedMultiItem(NotifyCollectionChangedEventArgs e)
{
NotifyCollectionChangedEventHandler handlers = this.CollectionChanged;
if( handlers != null )
foreach( NotifyCollectionChangedEventHandler handler in handlers.GetInvocationList() )
handler(this, !(handler.Target is ICollectionView) ? e : new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
#endregion
#region Extended Collection Methods
protected override void ClearItems()
{
if( this.Count == 0 ) return;
List<T> removed = new List<T>(this);
_SuppressCollectionChanged = true;
base.ClearItems();
_SuppressCollectionChanged = false;
OnCollectionChangedMultiItem(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, removed));
}
public void Add(IEnumerable<T> toAdd)
{
if( this == toAdd )
throw new Exception("Invalid operation. This would result in iterating over a collection as it is being modified.");
_SuppressCollectionChanged = true;
foreach( T item in toAdd )
Add(item);
_SuppressCollectionChanged = false;
OnCollectionChangedMultiItem(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, new List<T>(toAdd)));
}
public void Remove(IEnumerable<T> toRemove)
{
if( this == toRemove )
throw new Exception("Invalid operation. This would result in iterating over a collection as it is being modified.");
_SuppressCollectionChanged = true;
foreach( T item in toRemove )
Remove(item);
_SuppressCollectionChanged = false;
OnCollectionChangedMultiItem(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, new List<T>(toRemove)));
}
#endregion
}
发布于 2015-07-29 06:19:05
经过多次迭代之后,我们得到了这个版本的ObservableRangeCollection
和ReadOnlyObservableRangeCollection
,它们基于可接受的答案中的代码,在过去的6个月中我们不需要修改这些代码:
public class ObservableRangeCollection<T> : ObservableCollection<T>
{
private bool suppressNotification;
public ObservableRangeCollection() { }
public ObservableRangeCollection(IEnumerable<T> items)
: base(items)
{
}
public override event NotifyCollectionChangedEventHandler CollectionChanged;
protected virtual void OnCollectionChangedMultiItem(
NotifyCollectionChangedEventArgs e)
{
var handlers = CollectionChanged;
if (handlers == null) return;
foreach (NotifyCollectionChangedEventHandler handler in handlers.GetInvocationList())
{
if (handler.Target is ReadOnlyObservableCollection<T>
&& !(handler.Target is ReadOnlyObservableRangeCollection<T>))
{
throw new NotSupportedException(
"ObservableRangeCollection is wrapped in ReadOnlyObservableCollection which might be bound to ItemsControl " +
"which is internally using ListCollectionView which does not support range actions.\n" +
"Instead of ReadOnlyObservableCollection, use ReadOnlyObservableRangeCollection");
}
var collectionView = handler.Target as ICollectionView;
if (collectionView != null)
{
collectionView.Refresh();
}
else
{
handler(this, e);
}
}
}
protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
{
if (suppressNotification) return;
base.OnCollectionChanged(e);
if (CollectionChanged != null)
{
CollectionChanged.Invoke(this, e);
}
}
public void AddRange(IEnumerable<T> items)
{
if (items == null) return;
suppressNotification = true;
var itemList = items.ToList();
foreach (var item in itemList)
{
Add(item);
}
suppressNotification = false;
if (itemList.Any())
{
OnCollectionChangedMultiItem(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, itemList));
}
}
public void AddRange(params T[] items)
{
AddRange((IEnumerable<T>)items);
}
public void ReplaceWithRange(IEnumerable<T> items)
{
Items.Clear();
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
AddRange(items);
}
public void RemoveRange(IEnumerable<T> items)
{
suppressNotification = true;
var removableItems = items.Where(x => Items.Contains(x)).ToList();
foreach (var item in removableItems)
{
Remove(item);
}
suppressNotification = false;
if (removableItems.Any())
{
OnCollectionChangedMultiItem(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, removableItems));
}
}
}
public class ReadOnlyObservableRangeCollection<T> : ReadOnlyObservableCollection<T>
{
public ReadOnlyObservableRangeCollection(ObservableCollection<T> list)
: base(list)
{
}
protected override event NotifyCollectionChangedEventHandler CollectionChanged;
protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
{
var handlers = CollectionChanged;
if (handlers == null) return;
foreach (NotifyCollectionChangedEventHandler handler in handlers.GetInvocationList())
{
var collectionView = handler.Target as ICollectionView;
if (collectionView != null)
{
collectionView.Refresh();
}
else
{
handler(this, e);
}
}
}
}
我们基本上用ObservableRangeCollection
取代了应用程序中的所有ObservableRangeCollection
用法,它的工作原理就像一种魅力。
https://stackoverflow.com/questions/3300845
复制相似问题