使用MVVM中的后台工作程序更新ObservableCollection

时间:2010-09-02 15:10:44

标签: c# wpf multithreading observablecollection

好的,我最近实现了一个后台工作程序来执行数据的保存和加载。

然而,事实证明,将此工作用于保存命令很困难。

基本上,我的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);
    }

两个列表都是由一个单独的后台工作线程创建的。

3 个答案:

答案 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));
    }