如何销毁或分离CollectionView

时间:2015-02-10 19:10:28

标签: wpf collectionview inotifycollectionchanged

我观察到WPF ItemsControls的奇怪行为:如果将ItemsSource设置为实现INotifyCollectionChanged的对象,然后将ItemsSource设置为null,则创建为CollectionView向ItemsControl提供数据仍然监听源对象的CollectionChanged - 事件 如果现在通过不同的线程更改了源集合,则CollectionView会抛出异常(不附加到任何控件)。 虽然我理解为什么会这样,但我真的无法解决这种情况。

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

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

2 个答案:

答案 0 :(得分:3)

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

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

答案 1 :(得分:2)

<强>更新 看来,在.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中使用包装器。