好的,我最近实现了一个后台工作程序来执行数据的保存和加载。
然而,事实证明,将此工作用于保存命令很困难。
基本上,我的save命令会生成一个事件,该事件通知集合视图模型,已添加Item并且该项应添加到其自己的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);
}
两个列表都是由一个单独的后台工作线程创建的。
答案 0 :(得分:7)
如果您有代码将项目添加到可观察集合(可能在视图模型中),请在Add
调用中打包Dispatcher.BeginInvoke
。
不可否认,这意味着视图模型需要了解调度程序,然后测试就变得很难...幸运的是,引入自己的IDispatcher
接口并以正常方式使用依赖注入并不太难。 / p>
答案 1 :(得分:3)
这个怎么样?
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);
}
}
答案 2 :(得分:2)
我找到了blog post,它使用Dispatcher来管理所有ObeservableCollection的方法。以下是代码的片段,请参阅整个班级的post。
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));
}