使用BindingOperations.EnableCollectionSynchronization

Dil*_*hod 9 c# collections wpf multithreading observablecollection

我有两个WPF应用程序"UI","调试器"和一个ClassLibrary"BL".UI对Debugger和BL的引用.调试器引用BL.我在BL中收集了名为MyCollection的集合.UI应用程序启动调试器应用程序,调试器绑定到BL中的集合MyCollection.当我尝试从UI应用程序更改MyCollection集合时,我遇到异常.

A first chance exception of type 'System.NotSupportedException' occurred in PresentationFramework.dll

Additional information: This type of CollectionView does not support changes to its SourceCollection from a thread different from the Dispatcher thread.
Run Code Online (Sandbox Code Playgroud)

我在google搜索并发现:BindingOperations.EnableCollectionSynchronization 我无法弄清楚如何使用它.我不想引用我的BL项目中的任何UI dll.任何人都可以帮助我吗?

谢谢您的帮助!

Dre*_*kes 25

我在Stack Overflow上看到的所有示例都是错误的.你必须从另一个线程修改时,它锁定集合.

在调度程序(UI)线程上:

_itemsLock = new object();
Items = new ObservableCollection<Item>();
BindingOperations.EnableCollectionSynchronization(Items, _itemsLock);
Run Code Online (Sandbox Code Playgroud)

然后从另一个线程:

lock (_itemsLock)
{
    // Once locked, you can manipulate the collection safely from another thread
    Items.Add(new Item());
    Items.RemoveAt(0);
}
Run Code Online (Sandbox Code Playgroud)

本文中的更多信息:http:/ 10/ 10rem.net/blog/2012/01/20/wpf-45-cross-thread-collection-synchronization-redux


San*_*esh 5

我不确定这是否会有所帮助,但你仍然可以尝试一下.

添加PropertyDebugger其将持有CollectionBL

private ObservableCollection<string> _data = new ObservableCollection<string>();
private object _lock = new object();

public ObservableCollection<string> Data { get {return _data;} }
Run Code Online (Sandbox Code Playgroud)

在构造函数中只需添加以下行

BindingOperations.EnableCollectionSynchronization(_data, _lock);
Run Code Online (Sandbox Code Playgroud)

这将上面的线将照顾线程安全.

以下是示例

ViewModel(Debugger)

internal class ViewModelClass : INotifyPropertyChanged
{
    private object _lock = new object ();
    private ObservableCollection<string> _data;

    public ObservableCollection<string> Data
    {
        get { return _data; }
        private set
        {
            _data = value;
            RaisePropertyChanged ("Data");
        }
    }

    private string _enteredText;
    public string EnteredText
    {
        get { return _enteredText; }
        set
        {
            _enteredText = value;
            _data.Add (value); RaisePropertyChanged ("EnteredText");
        }
    }

    private void RaisePropertyChanged (string name)
    {
        var pc = PropertyChanged;
        if (pc != null)
            pc (this, new PropertyChangedEventArgs (name));
    }

    public ViewModelClass ()
    {
        var _model = new ModelClass ();
        Data = _model.Data;
        _data.CollectionChanged += (s, e) => RaisePropertyChanged ("Data");
    }

    public event PropertyChangedEventHandler PropertyChanged;
}
Run Code Online (Sandbox Code Playgroud)

型号(BL)

internal class ModelClass
{
    private ObservableCollection<string> _data;

    public ObservableCollection<string> Data
    {
        get { return _data; }
        private set { _data = value; }
    }

    public ModelClass ()
    {
        _data = new ObservableCollection<string> { "Test1", "Test2", "Test3" };
    }
}
Run Code Online (Sandbox Code Playgroud)

MainWindow.xaml.cs

public partial class MainWindow : Window
{
    public MainWindow ()
    {
        InitializeComponent ();
        this.DataContext = new ViewModelClass ();
    }
}
Run Code Online (Sandbox Code Playgroud)

MainWindow.xaml

<Window x:Class="CollectionSynchronizationTest.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            Title="MainWindow"
            Height="350"
            Width="525">
<StackPanel>
    <ComboBox IsEditable="True"
                        ItemsSource="{Binding Data}"
                        Text="{Binding EnteredText, Mode=TwoWay, UpdateSourceTrigger=LostFocus}" />
    <Button Content="Test" />
</StackPanel>
Run Code Online (Sandbox Code Playgroud)

当窗口加载时只需输入"SomeValue" ComboBox然后在按下Tab键后你应该在ComboBox下拉列表中找到新值

  • “BindingOperations.EnableCollectionSynchronization(_data,_lock);” 不考虑线程安全。您仍然必须锁定 _lock 对象。 (3认同)
  • @claudekennilol 是正确的:*您的*代码*必须*在您*对 ObservableCollection 进行的任何访问周围执行 `lock(_lock){ ... }`。`EnableCollectionSynchronization` 所做的只是让 WPF 使用与您相同的锁锁定 *其* 代码。该示例代码(危险地)不完整。 (3认同)
  • 为ObservableCollection <T>设置NotifyPropertyChanged有什么意义? (2认同)
  • ObservableCollection <T>为您做到这一点.所以你不需要将它用于ObservableCollections. (2认同)
  • 在修改来自另一个线程的集合时,您确实必须锁定lock对象(您不必分派给UI线程即可)。详情请参阅这篇文章:http://10rem.net/blog/2012/01/20/wpf-45-cross-thread-collection-synchronization-redux (2认同)