问题
I observe an odd behaviour of WPF ItemsControls: If a set the ItemsSource to an object which implements INotifyCollectionChanged
and after that set the ItemsSource
to null, the CollectionView
which was created to provide the data to the ItemsControl still listens to the CollectionChanged
-event of the source object.
If now the source collection is changed through a different thread, the CollectionView
throws an exception (without being attached to any control).
While I understand why this is happening, I’m really stuck resolving this situation.
Therefore the main question is, how can I destroy a CollectionView
so that it does not listen any more to CollectionChanged
-event. Or how can I disable it doing that / detaching the underlying collection.
Please note: The described behavior is not with ObservableCollection
. The source object is an IEnumerable
of T and implements INotifyCollectionChanged
.
回答1:
You're looking for the CollectionView.DetachFromSourceCollection() method:
var collectionView = CollectionViewSource.GetDefaultView(yourEnumerable) as CollectionView;
collectionView.DetachFromSourceCollection();
回答2:
Update It seems, that under .net 4.5 there is this desired functionality. See the answer of HighCore. For those not having 4.5 I leave my workaround here, maybe it helps someone:
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;
}
}
To use this, set the Wrapper as the ItemsSource
of the ItemsControl. Before setting then the ItemsSource
to null, call Detach on the wrapper to unregister the changed-event. Something as follows:
var wrapper = m_lstLog.ItemsSource as DetachableNotifyCollectionChangedWrapper;
if (null != wrapper) wrapper.Detach();
m_lstLog.ItemsSource = null;
The wrapper can also be used from within a ViewModel.
来源:https://stackoverflow.com/questions/28439865/how-to-destroy-or-detach-a-collectionview