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

发布于 2024-09-17 10:25:53 字数 1834 浏览 6 评论 0原文

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

然而,事实证明,让它在保存命令上运行是很困难的。

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

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

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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

不奢求什么 2024-09-24 10:25:53

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

不可否认,这意味着视图模型需要了解调度程序,这使得测试变得很困难...幸运的是,引入您自己的 IDispatcher 接口并以正常方式使用依赖项注入并不太难。

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.

不寐倦长更 2024-09-24 10:25:53

这个怎么样?

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

How about this?

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);
    }
}
赢得她心 2024-09-24 10:25:53

我发现了一篇博客文章,它使用调度程序管理 ObeservableCollection 的所有方法。这是代码片段,请参阅帖子< /a> 对于整个班级。

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

I found a blog post that uses the Dispatcher to manage all of the ObeservableCollection's methods. Here is a snip-it of the code, see the post for the entire class.

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));
    }
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文