using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; using System.Windows.Threading; namespace AllBase.Util { public class ObservableCollectionThreadSafe : ObservableCollection, IDisposable { #region Data private Dispatcher _dispatcher; private ReaderWriterLockSlim _lock; #endregion #region Constructor public ObservableCollectionThreadSafe() { _dispatcher = Dispatcher.CurrentDispatcher; _lock = new ReaderWriterLockSlim(); } #endregion #region Overrides /// /// Clear all items /// protected override void ClearItems() { _dispatcher.InvokeIfRequired(() => { _lock.EnterWriteLock(); try { base.ClearItems(); } finally { _lock.ExitWriteLock(); } }, DispatcherPriority.DataBind); } /// /// Inserts an item /// protected override void InsertItem(int index, T item) { _dispatcher.InvokeIfRequired(() => { if (index > this.Count) return; _lock.EnterWriteLock(); try { base.InsertItem(index, item); } finally { _lock.ExitWriteLock(); } }, DispatcherPriority.DataBind); } /// /// Moves an item /// protected override void MoveItem(int oldIndex, int newIndex) { _dispatcher.InvokeIfRequired(() => { _lock.EnterReadLock(); int itemCount = this.Count; _lock.ExitReadLock(); if (oldIndex >= itemCount | newIndex >= itemCount | oldIndex == newIndex) return; _lock.EnterWriteLock(); try { base.MoveItem(oldIndex, newIndex); } finally { _lock.ExitWriteLock(); } }, DispatcherPriority.DataBind); } /// /// Removes an item /// protected override void RemoveItem(int index) { _dispatcher.InvokeIfRequired(() => { if (index >= this.Count) return; _lock.EnterWriteLock(); try { base.RemoveItem(index); } finally { _lock.ExitWriteLock(); } }, DispatcherPriority.DataBind); } /// /// Sets an item /// protected override void SetItem(int index, T item) { _dispatcher.InvokeIfRequired(() => { _lock.EnterWriteLock(); try { base.SetItem(index, item); } finally { _lock.ExitWriteLock(); } }, DispatcherPriority.DataBind); } #endregion #region Public Methods /// /// Return as a cloned copy of this Collection /// public T[] ToSyncArray() { _lock.EnterReadLock(); try { T[] _sync = new T[this.Count]; this.CopyTo(_sync, 0); return _sync; } finally { _lock.ExitReadLock(); } } #region IDisposable Support private bool disposedValue = false; // To detect redundant calls protected virtual void Dispose(bool disposing) { if (!disposedValue) { if (disposing) { if (_lock != null) _lock.Dispose(); } // TODO: free unmanaged resources (unmanaged objects) and override a finalizer below. // TODO: set large fields to null. disposedValue = true; } } // TODO: override a finalize only if Dispose(bool disposing) above has code to free unmanaged resources. // ~ObservableCollectionThreadSafe() { // // Do not change this code. Put cleanup code in Dispose(bool disposing) above. // Dispose(false); // } // This code added to correctly implement the disposable pattern. public void Dispose() { // Do not change this code. Put cleanup code in Dispose(bool disposing) above. Dispose(true); // TODO: uncomment the following line if the finalize is overridden above. // GC.SuppressFinalize(this); } #endregion #endregion } /// /// WPF Threading extension methods /// public static class WPFControlThreadingExtensions { #region Public Methods /// /// A simple WPF threading extension method, to invoke a delegate /// on the correct thread if it is not currently on the correct thread /// Which can be used with DispatcherObject types /// /// The Dispatcher object on which to do the Invoke /// The delegate to run /// The DispatcherPriority public static void InvokeIfRequired(this Dispatcher disp, Action performAction, DispatcherPriority priority) { if (disp.Thread != Thread.CurrentThread) { disp.Invoke(priority, performAction); } else performAction(); } #endregion } }