【问题标题】:Update an ObservableCollection with a background worker in MVVM使用 MVVM 中的后台工作人员更新 ObservableCollection
【发布时间】:2010-09-02 15:10:43
【问题描述】:

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

但是,事实证明,要让它在保存命令上工作很困难。

基本上,我的保存命令会生成一个事件,通知集合视图模型,一个项目已被添加并且该项目应该被添加到它自己的 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);
    }

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

【问题讨论】:

    标签: c# wpf multithreading observablecollection


    【解决方案1】:

    如果您有将项目添加到可观察集合的代码(可能在视图模型中),请将 Add 调用包装在 Dispatcher.BeginInvoke 调用中。

    诚然,这意味着视图模型需要了解调度程序,然后测试变得很尴尬……幸运的是,引入自己的IDispatcher 接口并以正常方式使用依赖注入并不难。

    【讨论】:

    • 嗨,乔恩,谢谢你的回复,我已经有一个集合对象,它继承自 ObserveableCollection 上的 InsertItem 这将执行 Dispatcher.CheckAccess,如果为 false,则执行 Dispather.BeginInvoke,但这仍然不起作用?
    • @jpgooner:你确定这个代码真的被使用了吗?你能想出一个简短但完整的例子来说明这个问题吗?
    • 我在上面添加了更多细节,如果您需要更多信息,请告诉我,我还是 stackoverflow 的新手
    • 感谢您的回复,因为后台线程正在创建一些集合检查访问是否返回 true,但实际上我需要与 UI 线程进行比较!感谢您的帮助!
    【解决方案2】:

    这个怎么样?

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

    【讨论】:

      【解决方案3】:

      我找到了一个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));
          }
      

      【讨论】:

      • 好例子!只是一个小的更正:受保护的覆盖无效InsertItem(int index,T item){DoDispatchedAction(()=> BaseInsertItem(index,item)); } 实际上是受保护的 override void InsertItem(int index, T item) { DoDispatchedAction(() => base.InsertItem(index, item)); }
      • 谢谢。我不确定为什么我显式创建了一个方法并在第二个示例中使用了匿名方法。我已经把它们清理干净了。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-08-14
      • 1970-01-01
      • 2010-12-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多