从另一个线程更新 ObservableCollection 的最佳/最干净的策略

Dom*_*nas 0 c# wpf multithreading dispatcher observablecollection

通常我会检查我是否可以访问ObservableCollection,如果没有,我会调用一个Dispatcher. 这里(上Stackoverflow)还有其他一些解决方案,但我不知道什么是最好和最干净的方法。我认为我的解决方案已经过时,不应再使用。

在我的例子中是ItemsCollection绑定到UI. 该_UpdateTheCollectionFromAnotherThread()会被调用(从另一个thread或将开启另一个thread)并保存数据暂时到items。之后,我会检查访问权限并dispatcher在需要时调用 a 。

这是我必须走的路还是有一些更好更清洁的解决方案?

FooClass

public class FooClass
{
    // ##############################################################################################################################
    // Properties
    // ##############################################################################################################################

    /// <summary>
    /// This Items are bound to my UI.
    /// </summary>
    public ObservableCollection<string> ItemsCollection { get; } = new ObservableCollection<string>();

    // ##############################################################################################################################
    // Singleton pattern
    // ##############################################################################################################################

    /// <summary>
    /// The instance of <see cref="FooClass"/>.
    /// </summary>
    internal static FooClass Instance => _Instance ?? (_Instance = new FooClass());
    private static FooClass _Instance;

    // ##############################################################################################################################
    // Konstruktor
    // ##############################################################################################################################

    private FooClass()
    {

    }


    // ##############################################################################################################################
    // Method
    // ##############################################################################################################################

    private void _UpdateTheCollectionFromAnotherThread()
    {
        List<string> items = new List<string>();

        //Here would be some logic to create and fill the items list....


        //and now apply them to the public list

        if (System.Windows.Application.Current.Dispatcher.CheckAccess())
        {
            ItemsCollection.Clear();
            foreach (string item in items)
            {
                ItemsCollection.Add(item);
            }
        }
        else
        {
            System.Windows.Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(() =>
            {
                ItemsCollection.Clear();
                foreach (string item in items)
                {
                    ItemsCollection.Add(item);
                }
            }));
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

mm8*_*mm8 5

如果您使用的是 .NET Framework 4.5 或更高版本,则可以通过调用该BindingOperations.EnableCollectionSynchronization方法启用跨多个线程访问集合。请注意,必须在 UI 线程上调用此方法:

public class FooClass
{
    private readonly object _lock = new object();
    public ObservableCollection<string> ItemsCollection { get; } = new ObservableCollection<string>();

    internal static FooClass Instance => _Instance ?? (_Instance = new FooClass());
    private static FooClass _Instance;

    private FooClass()
    {
        BindingOperations.EnableCollectionSynchronization(ItemsCollection, _lock);
    }

    private void _UpdateTheCollectionFromAnotherThread()
    {
        List<string> items = new List<string>();
        ItemsCollection.Clear();
        foreach (string item in items)
        {
            ItemsCollection.Add(item);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

如果FooClass可能是在后台线程上创建的,您仍然可以通过不重复自己来改进代码:

public class FooClass
{
    public ObservableCollection<string> ItemsCollection { get; } = new ObservableCollection<string>();

    internal static FooClass Instance => _Instance ?? (_Instance = new FooClass());
    private static FooClass _Instance;

    private FooClass() { }

    private void _UpdateTheCollectionFromAnotherThread()
    {
        List<string> items = new List<string>();

        if (System.Windows.Application.Current.Dispatcher.CheckAccess())
            ClearCollection(items);
        else
            System.Windows.Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(() => ClearCollection(items)));
    }

    private void ClearCollection(List<string> items)
    {
        ItemsCollection.Clear();
        foreach (string item in items)
        {
            ItemsCollection.Add(item);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)