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.
Basically, my save command generates an event, that notifies a collection view model, that an Item has been added and that the item should be added to its own ObservableCollection.
At this point, I get the usual exception saying I can NOT update an ICollection on a different thread. I have tried creating a new list type that calls Dispatcher.Invoke
, however this still generates the same exception.
I was wondering whether anyone else has any suggestions on how best to tackle this?
So currently I have a class that Inherits from ObservableCollection:
public class ThreadSafeObservableCollection<T> : ObservableCollection<T>
{
public ThreadSafeObservableCollection(List<T> collection)
: base(collection)
{
dispatcher = Dispatcher.CurrentDispatcher;
rwLock = new ReaderWriterLock();
}
protected override void InsertItem(int index, T item)
{
if (dispatcher.CheckAccess())
{
if (index > this.Count)
return;
LockCookie c = rwLock.UpgradeToWriterLock(-1);
base.InsertItem(index, item);
rwLock.DowngradeFromWriterLock(ref c);
}
else
{
object[] obj = new object[] { index, item };
dispatcher.Invoke(
DispatcherPriority.Send,
(SendOrPostCallback)delegate { InsertItemImpl(obj); },
obj);
}
}
I then have a view model class that has a background worker which carries out the save.
Once the save is complete, an event is fired to another view model to update its list.
protected override void OnObjectAddedToRepository(object sender, ObjectEventArgs<cdAdministrators> e)
{
Dispatcher x = Dispatcher.CurrentDispatcher;
var viewModel = new AdministratorViewModel(e.EventObject, DataAccess);
viewModel.RecentlyAdded = true;
viewModel.ItemSelected += this.OnItemSelected;
this.AllViewModels.Add(viewModel);
RecentlyAddedViewModel = viewModel;
OnPropertyChanged(null);
}
Both lists are created by a separate background worker thread.
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.