232 lines
6.3 KiB
C#
232 lines
6.3 KiB
C#
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<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
|
|
}
|
|
}
|