Here is a compilation of some of the decisions I made. The idea of the collection changed the introcation taken from the first answer.
It also seems that the "Reset" operation should be synchronous with the main thread, otherwise strange things happen with CollectionView and CollectionViewSource.
I think that since on "Reset" the handler tries to read the contents of the collection immediately, and they should already be in place. If you perform a “Reset” async and immediately add some items that are asynchronous, than recently added items can be added twice.
public interface IObservableList<T> : IList<T>, INotifyCollectionChanged { } public class ObservableList<T> : IObservableList<T> { private IList<T> collection = new List<T>(); public event NotifyCollectionChangedEventHandler CollectionChanged; private ReaderWriterLock sync = new ReaderWriterLock(); protected virtual void OnCollectionChanged(NotifyCollectionChangedEventArgs args) { if (CollectionChanged == null) return; foreach (NotifyCollectionChangedEventHandler handler in CollectionChanged.GetInvocationList()) { // If the subscriber is a DispatcherObject and different thread. var dispatcherObject = handler.Target as DispatcherObject; if (dispatcherObject != null && !dispatcherObject.CheckAccess()) { if ( args.Action == NotifyCollectionChangedAction.Reset ) dispatcherObject.Dispatcher.Invoke (DispatcherPriority.DataBind, handler, this, args); else // Invoke handler in the target dispatcher thread... // asynchronously for better responsiveness. dispatcherObject.Dispatcher.BeginInvoke (DispatcherPriority.DataBind, handler, this, args); } else { // Execute handler as is. handler(this, args); } } } public ObservableList() { } public void Add(T item) { sync.AcquireWriterLock(Timeout.Infinite); try { collection.Add(item); OnCollectionChanged( new NotifyCollectionChangedEventArgs( NotifyCollectionChangedAction.Add, item)); } finally { sync.ReleaseWriterLock(); } } public void Clear() { sync.AcquireWriterLock(Timeout.Infinite); try { collection.Clear(); OnCollectionChanged( new NotifyCollectionChangedEventArgs( NotifyCollectionChangedAction.Reset)); } finally { sync.ReleaseWriterLock(); } } public bool Contains(T item) { sync.AcquireReaderLock(Timeout.Infinite); try { var result = collection.Contains(item); return result; } finally { sync.ReleaseReaderLock(); } } public void CopyTo(T[] array, int arrayIndex) { sync.AcquireWriterLock(Timeout.Infinite); try { collection.CopyTo(array, arrayIndex); } finally { sync.ReleaseWriterLock(); } } public int Count { get { sync.AcquireReaderLock(Timeout.Infinite); try { return collection.Count; } finally { sync.ReleaseReaderLock(); } } } public bool IsReadOnly { get { return collection.IsReadOnly; } } public bool Remove(T item) { sync.AcquireWriterLock(Timeout.Infinite); try { var index = collection.IndexOf(item); if (index == -1) return false; var result = collection.Remove(item); if (result) OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, item, index)); return result; } finally { sync.ReleaseWriterLock(); } } public IEnumerator<T> GetEnumerator() { return collection.GetEnumerator(); } System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { return collection.GetEnumerator(); } public int IndexOf(T item) { sync.AcquireReaderLock(Timeout.Infinite); try { var result = collection.IndexOf(item); return result; } finally { sync.ReleaseReaderLock(); } } public void Insert(int index, T item) { sync.AcquireWriterLock(Timeout.Infinite); try { collection.Insert(index, item); OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item, index)); } finally { sync.ReleaseWriterLock(); } } public void RemoveAt(int index) { sync.AcquireWriterLock(Timeout.Infinite); try { if (collection.Count == 0 || collection.Count <= index) return; var item = collection[index]; collection.RemoveAt(index); OnCollectionChanged( new NotifyCollectionChangedEventArgs( NotifyCollectionChangedAction.Remove, item, index)); } finally { sync.ReleaseWriterLock(); } } public T this[int index] { get { sync.AcquireReaderLock(Timeout.Infinite); try { var result = collection[index]; return result; } finally { sync.ReleaseReaderLock(); } } set { sync.AcquireWriterLock(Timeout.Infinite); try { if (collection.Count == 0 || collection.Count <= index) return; var item = collection[index]; collection[index] = value; OnCollectionChanged( new NotifyCollectionChangedEventArgs( NotifyCollectionChangedAction.Replace, value, item, index)); } finally { sync.ReleaseWriterLock(); } } } }
norekhov Apr 3 '14 at 12:34 on 2014-04-03 12:34
source share