Обновление коллекции ObservableCollection с помощью фонового рабочего в MVVM
Хорошо, я недавно реализовал фоновый рабочий для выполнения сохранения и загрузки данных.
Однако заставить это работать с командой save оказалось трудно.
В принципе, моя команда save генерирует событие, которое уведомляет модель представления коллекции о том, что элемент был добавлен и что элемент должен быть добавлен в свою собственную коллекцию ObservableCollection.
В этот момент я получаю обычное исключение, говорящее, что я не могу обновить ICollection в другом потоке. Я уже пробовал создание нового типа списка, который вызывает Dispatcher.Invoke
, однако это все равно создает то же исключение.
Итак, в настоящее время у меня есть класс, который наследует от 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);
}
}
Затем у меня есть класс модели представления, у которого есть фоновый работник, который выполняет сохранение.
После завершения сохранения событие запускается в другую модель представления для обновления ее списка.
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);
}
Оба списки создаются отдельным фоновым рабочим потоком.
3 ответа:
Если у вас есть код, который добавляет элемент в наблюдаемую коллекцию (предположительно в модели представления), оберните этот вызов
По общему признанию, это означает, что модель представления должна знать о диспетчере, который затем становится неудобным для тестирования... к счастью, не так уж сложно ввести свой собственный интерфейсAdd
в ВызовDispatcher.BeginInvoke
.IDispatcher
и использовать инъекцию зависимостей обычным способом.
Как насчет этого?
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); } }
Я нашел сообщение в блоге , которое использует диспетчер для управления всеми методами ObeservableCollection. Вот фрагмент кода, смотрите пост для всего класса.
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)); }