public class ObservableCollectionThreadSafe<T>
: ObservableCollection<T>, IDisposable
{
#region Data
private Dispatcher _dispatcher;
private ReaderWriterLockSlim _lock;
#endregion
#region Constructor
public ObservableCollectionThreadSafe()
{
_dispatcher = Dispatcher.CurrentDispatcher;
_lock = new ReaderWriterLockSlim();
}
#endregion
#region Overrides
/// <summary>
/// Clear all items
/// </summary>
protected override void ClearItems()
{
_dispatcher.InvokeIfRequired(() =>
{
_lock.EnterWriteLock();
try
{
base.ClearItems();
}
finally
{
_lock.ExitWriteLock();
}
}, DispatcherPriority.DataBind);
}
/// <summary>
/// Inserts an item
/// </summary>
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);
}
/// <summary>
/// Moves an item
/// </summary>
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);
}
/// <summary>
/// Removes an item
/// </summary>
protected override void RemoveItem(int index)
{
_dispatcher.InvokeIfRequired(() =>
{
if (index >= this.Count)
return;
_lock.EnterWriteLock();
try
{
base.RemoveItem(index);
}
finally
{
_lock.ExitWriteLock();
}
}, DispatcherPriority.DataBind);
}
/// <summary>
/// Sets an item
/// </summary>
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
/// <summary>
/// Return as a cloned copy of this Collection
/// </summary>
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
}
/// <summary>
/// WPF Threading extension methods
/// </summary>
public static class WPFControlThreadingExtensions
{
#region Public Methods
/// <summary>
/// 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
/// </summary>
/// <param name="disp">The Dispatcher object on which to do the Invoke</param>
/// <param name="performAction">The delegate to run</param>
/// <param name="priority">The DispatcherPriority</param>
public static void InvokeIfRequired(this Dispatcher disp,
Action performAction, DispatcherPriority priority)
{
if (disp.Thread != Thread.CurrentThread)
{
disp.Invoke(priority, performAction);
}
else
performAction();
}
#endregion
}