How to destroy or detach a CollectionView
Asked Answered
H

2

3

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.

Houphouetboigny answered 10/2, 2015 at 19:10 Comment(5)
Does calling clear() not suffice in your case, you can then set an empty template when Count == 0.Harriet
Clear on what? The source object?Houphouetboigny
rather than setting the collection to null. Sorry should have clarified on this point. EDIT but I do recall that there is an issue with view vot picking up changes when you call the clear method in ObservableCollection, I'll find that post and comeback to you with more info.Harriet
Ah, found the post and it basically implies that if your objects in your list are complex and have some sort of Handlers attached to them it will be difficult to detach the handlers. Here is the post.Harriet
@XAMIMAX: it's not an ObservableCollection, but I've found a solution, see below. Thanks anyway!Houphouetboigny
T
3

You're looking for the CollectionView.DetachFromSourceCollection() method:

var collectionView = CollectionViewSource.GetDefaultView(yourEnumerable) as CollectionView;
collectionView.DetachFromSourceCollection();
Tarkany answered 10/2, 2015 at 19:53 Comment(1)
Yes, exactly! But sadly it seems that this method only is available in .net 4.5 and the app I'm working currently on is 4.0.Houphouetboigny
H
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.

Houphouetboigny answered 10/2, 2015 at 19:35 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.