Using ObservableCollection across UI and Non-UI threads

我是研究僧i 提交于 2021-02-11 13:38:57

问题


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

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.'

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.

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

回答1:


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?

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.

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.



来源:https://stackoverflow.com/questions/51348527/using-observablecollection-across-ui-and-non-ui-threads

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!