跨 UI 和非 UI 线程使用 ObservableCollection [英] Using ObservableCollection across UI and Non-UI threads

查看:71
本文介绍了跨 UI 和非 UI 线程使用 ObservableCollection的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用此处所述的SortableObservableCollection.现在我想从 UI 或非 UI 线程操作它,所以我尝试了描述的解决方案 这里:

I am using the SortableObservableCollection described here. Now I'd like to manipulate it from a UI or a non-UI thread so I tried the solution described here:

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Runtime;
using System.Windows.Data;

public class SortableObservableCollection<T> : ObservableCollection<T>
{
    private object _itemsLock = new object();

    public SortableObservableCollection()
        : base()
    {
        BindingOperations.EnableCollectionSynchronization(this, _itemsLock);
    }

    public SortableObservableCollection(List<T> l) : base(l)
    {
        BindingOperations.EnableCollectionSynchronization(this, _itemsLock);
    }

    public SortableObservableCollection(IEnumerable<T> l) : base(l)
    {
        BindingOperations.EnableCollectionSynchronization(this, _itemsLock);
    }
    #region Sorting

    public void Sort<TKey>(Func<T, TKey> keySelector)
    {
        InternalSort(Items.OrderBy(keySelector));
    }

    public void SortDescending<TKey>(Func<T, TKey> keySelector)
    {
        InternalSort(Items.OrderByDescending(keySelector));
    }

    public void Sort<TKey>(Func<T, TKey> keySelector, IComparer<TKey> comparer)
    {
        InternalSort(Items.OrderBy(keySelector, comparer));
    }

    private void InternalSort(IEnumerable<T> sortedItems)
    {
        var sortedItemsList = sortedItems.ToList();

        foreach (var item in sortedItemsList)
        {
            Move(IndexOf(item), sortedItemsList.IndexOf(item));
        }
    }
    #endregion // Sorting

    public new void Add(T item)
    {
        base.Add(item);
    }

    [TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")]
    public new void Clear()
    {
        base.Clear();
    }

    public new bool Remove(T item)
    {
        return base.Remove(item);
    }
}

但是在 .Net 4.5.2、WPF 桌面应用程序中的这个解决方案在移除、添加和清除方法中抛出异常:System.NotSupportedException:'这种类型的 CollectionView 不支持更改从一个不同于 Dispatcher 线程的线程到它的 SourceCollection.'

But this solution in .Net 4.5.2, WPF Desktop Application, throws an exception in the Remove, Add, and Clear method: System.NotSupportedException: 'This type of CollectionView does not support changes to its SourceCollection from a thread different from the Dispatcher thread.'

因此,我重新使用 Application.Current.Dispatcher 方法(见下文),因为 BindingOperations.EnableCollectionSynchronization(this, _itemsLock); 机制不像链接的博客文章中宣传的那样工作

So, I went back to using the Application.Current.Dispatcher approach (see below) since the BindingOperations.EnableCollectionSynchronization(this, _itemsLock); mechanism does not work as advertised in the blog post linked above.

我的问题是:我在第一个列表中做错了什么,还是博客文章不正确,声称线程边界总是可以通过这种机制成功跨越?下面的清单是最好的,还是有更好的解决方案来管理具有跨线程绑定的 observablecollection?

My question is: Am I doing something wrong in the first listing or is the blog post incorrect with the claim that thread boundaries can always succesfully be crossed with this mechanism? Is the listing below the best one can do or is there a better solution for managing an observablecollection with bindings across threads?

public class SortableObservableCollection<T> : ObservableCollection<T>
{
    public SortableObservableCollection()
        : base()
    {
    }

    public SortableObservableCollection(List<T> l) : base(l)
    {
    }

    public SortableObservableCollection(IEnumerable<T> l) : base(l)
    {
    }
    #region Sorting

    public void Sort<TKey>(Func<T, TKey> keySelector)
    {
        InternalSort(Items.OrderBy(keySelector));
    }

    public void SortDescending<TKey>(Func<T, TKey> keySelector)
    {
        InternalSort(Items.OrderByDescending(keySelector));
    }

    public void Sort<TKey>(Func<T, TKey> keySelector, IComparer<TKey> comparer)
    {
        InternalSort(Items.OrderBy(keySelector, comparer));
    }

    private void InternalSort(IEnumerable<T> sortedItems)
    {
        var sortedItemsList = sortedItems.ToList();

        foreach (var item in sortedItemsList)
        {
            Move(IndexOf(item), sortedItemsList.IndexOf(item));
        }
    }
    #endregion // Sorting

    public new void Add(T item)
    {
        Application.Current.Dispatcher.Invoke(() =>
        {
            base.Add(item);
        }, System.Windows.Threading.DispatcherPriority.DataBind);
    }

    [TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")]
    public new void Clear()
    {
        Application.Current.Dispatcher.Invoke(() =>
        {
            base.Clear();
        }, System.Windows.Threading.DispatcherPriority.DataBind);
    }

    public new bool Remove(T item)
    {
        return Application.Current.Dispatcher.Invoke(() =>
        {
            return base.Remove(item);
        }, System.Windows.Threading.DispatcherPriority.DataBind);
    }
}

推荐答案

我的问题是:我在第一个列表中做错了什么,还是博客文章不正确,声称线程边界总是可以通过这种机制成功跨越?

My question is: Am I doing something wrong in the first listing or is the blog post incorrect with the claim that thread boundaries can always succesfully be crossed with this mechanism?

问题是您需要在 UI 线程上调用 BindingOperations.EnableCollectionSynchronization 方法,即您需要在 UI 线程上实例化您的 SortableObservableCollection这种工作方式.

The thing is that you need to call the BindingOperations.EnableCollectionSynchronization method on the UI thread, i.e. you need to instantiate your SortableObservableCollection<T> on the UI thread for this approach to work.

如果您不能保证集合将在 UI 线程上初始化,您应该使用调度程序将所有修改数据绑定集合的操作编组回 UI 线程.在后台线程上调用 BindingOperations.EnableCollectionSynchronization 不会解决您的问题.

If you can't guarantee that the collection will be initialized on the UI thread you should use the dispatcher to marshal all operations that modifies the data-bound collection back to the UI thread. Calling BindingOperations.EnableCollectionSynchronization on a background thread won't solve your issue.

这篇关于跨 UI 和非 UI 线程使用 ObservableCollection的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆