跨 UI 和非 UI 线程使用 ObservableCollection

Using ObservableCollection across UI and Non-UI threads

我正在使用描述的 SortableObservableCollection 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:'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); 机制不起作用正如上面链接的博客 post 中所宣传的那样。

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

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<T>工作方式。

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