Ok, I recently implemented a background worker to perform saving and loading of data.
However, getting this to work on a save command has proved difficult.
B
I found a blog post that uses the Dispatcher to manage all of the ObeservableCollection's methods. Here is a snip-it of the code, see the post for the entire class.
public class DispatchingObservableCollection<T> : ObservableCollection<T>
{
/// <summary>
/// The default constructor of the ObservableCollection
/// </summary>
public DispatchingObservableCollection()
{
//Assign the current Dispatcher (owner of the collection)
_currentDispatcher = Dispatcher.CurrentDispatcher;
}
private readonly Dispatcher _currentDispatcher;
/// <summary>
/// Executes this action in the right thread
/// </summary>
///<param name="action">The action which should be executed</param>
private void DoDispatchedAction(Action action)
{
if (_currentDispatcher.CheckAccess())
action();
else
_currentDispatcher.Invoke(DispatcherPriority.DataBind, action);
}
/// <summary>
/// Clears all items
/// </summary>
protected override void ClearItems()
{
DoDispatchedAction(() => base.ClearItems());
}
/// <summary>
/// Inserts a item at the specified index
/// </summary>
///<param name="index">The index where the item should be inserted</param>
///<param name="item">The item which should be inserted</param>
protected override void InsertItem(int index, T item)
{
DoDispatchedAction(() => base.InsertItem(index, item));
}
Where you've got code which adds the item to the observable collection (presumably in the view model), wrap that Add
call in a Dispatcher.BeginInvoke
call.
Admittedly that means the view model needs to know about the dispatcher, which then becomes awkward to test... fortunately it's not too hard to introduce your own IDispatcher
interface and use dependency injection in the normal way.
How about this?
public class ThreadSafeObservableCollection<T> : ObservableCollection<T>
{
private SynchronizationContext SynchronizationContext;
public ThreadSafeObservableCollection()
{
SynchronizationContext = SynchronizationContext.Current;
// current synchronization context will be null if we're not in UI Thread
if (SynchronizationContext == null)
throw new InvalidOperationException("This collection must be instantiated from UI Thread, if not, you have to pass SynchronizationContext to con structor.");
}
public ThreadSafeObservableCollection(SynchronizationContext synchronizationContext)
{
if (synchronizationContext == null)
throw new ArgumentNullException("synchronizationContext");
this.SynchronizationContext = synchronizationContext;
}
protected override void ClearItems()
{
this.SynchronizationContext.Send(new SendOrPostCallback((param) => base.ClearItems()), null);
}
protected override void InsertItem(int index, T item)
{
this.SynchronizationContext.Send(new SendOrPostCallback((param) => base.InsertItem(index, item)), null);
}
protected override void RemoveItem(int index)
{
this.SynchronizationContext.Send(new SendOrPostCallback((param) => base.RemoveItem(index)), null);
}
protected override void SetItem(int index, T item)
{
this.SynchronizationContext.Send(new SendOrPostCallback((param) => base.SetItem(index, item)), null);
}
protected override void MoveItem(int oldIndex, int newIndex)
{
this.SynchronizationContext.Send(new SendOrPostCallback((param) => base.MoveItem(oldIndex, newIndex)), null);
}
}