如何销毁或分离 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();
我观察到 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();