如何销毁或分离 CollectionView

How to destroy or detach a CollectionView

我观察到 WPF ItemsControls 的一个奇怪行为:如果将 ItemsSource 设置为实现 INotifyCollectionChanged 的对象,然后将 ItemsSource 设置为 null,则 CollectionView 是为向 ItemsControl 提供数据而创建的仍然侦听源对象的 CollectionChanged 事件。
如果现在通过不同的线程更改源集合,则 CollectionView 会抛出异常(不附加到任何控件)。 虽然我明白为什么会这样,但我真的很难解决这个问题。

因此主要问题是,我怎样才能销毁 CollectionView 以便它不再监听 CollectionChanged 事件。或者我怎样才能禁用它/分离底层集合。

请注意:所描述的行为不适用于 ObservableCollection。源对象是 T 的 IEnumerable 并实现 INotifyCollectionChanged

更新 看来,在 .net 4.5 下有这个所需的功能。请参阅 HighCore 的答案。对于那些没有 4.5 的人,我把我的解决方法留在这里,也许它对某人有帮助:

class DetachableNotifyCollectionChangedWrapper : IEnumerable, INotifyCollectionChanged {
        IEnumerable m_source;
        public event NotifyCollectionChangedEventHandler CollectionChanged;
        public DetachableNotifyCollectionChangedWrapper(IEnumerable enumerable) {
            if (null == enumerable) throw new ArgumentNullException("enumerable"); ;
            m_source = enumerable;
            var ncc = m_source as INotifyCollectionChanged;
            if (null != ncc) ncc.CollectionChanged += SourceCollectionChanged;
        }
        void SourceCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) {
            if (null != CollectionChanged) CollectionChanged(this,e);
        }
        public IEnumerator GetEnumerator() {
            return m_source.GetEnumerator();
        }
        public void Detach() {
            var ncc = m_source as INotifyCollectionChanged;
            if (null != ncc) ncc.CollectionChanged -= SourceCollectionChanged;
        }            
}

要使用它,请将 Wrapper 设置为 ItemsControl 的 ItemsSource。在将 ItemsSource 设置为 null 之前,调用包装器上的 Detach 以注销已更改的事件。内容如下:

var wrapper = m_lstLog.ItemsSource as DetachableNotifyCollectionChangedWrapper;
if (null != wrapper) wrapper.Detach();
m_lstLog.ItemsSource = null;    

包装器也可以在 ViewModel 中使用。

您正在寻找 CollectionView.DetachFromSourceCollection() 方法:

var collectionView = CollectionViewSource.GetDefaultView(yourEnumerable) as CollectionView;
collectionView.DetachFromSourceCollection();