使用BindingOperations.EnableCollectionSynchronization

时间:2014-02-12 06:47:50

标签: c# wpf multithreading collections observablecollection

我有两个WPF应用程序“UI”,“Debugger”和一个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.

我在谷歌上搜索,发现了这个:BindingOperations.EnableCollectionSynchronization 我无法弄清楚如何使用它。我不想引用我的BL项目中的任何UI dll。有人能协助我吗?

感谢您的帮助!

5 个答案:

答案 0 :(得分:21)

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

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

_itemsLock = new object();
Items = new ObservableCollection<Item>();
BindingOperations.EnableCollectionSynchronization(Items, _itemsLock);

然后从另一个线程:

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

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

答案 1 :(得分:5)

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

Property中添加Debugger,其中CollectionBL类似

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

public ObservableCollection<string> Data { get {return _data;} }

在构造函数中添加以下行

BindingOperations.EnableCollectionSynchronization(_data, _lock);

这将在线上方处理线程安全。

以下是示例

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

模型(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" };
    }
}

MainWindow.xaml.cs

public partial class MainWindow : Window
{
    public MainWindow ()
    {
        InitializeComponent ();
        this.DataContext = new ViewModelClass ();
    }
}

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>

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

答案 2 :(得分:1)

在这个blog中,您可以找到一个如何使用BindingOperations的简单教程......这很容易。

答案 3 :(得分:0)

WPF应用程序可以使用ItemsControl或其子类之一(ListBox,DataGrid,TreeView,ListView等)显示数据集合。 WPF通过CollectionView的子类将其对集合的所有访问权限引导到。 ItemsControl和CollectionView都与创建ItemsControl的线程有关联,这意味着禁止在其他线程上使用它们并引发异常。实际上,此限制也适用于集合。您可能要在多个线程上使用集合。例如,您想在“数据收集”线程上更新集合(添加或删除项目),同时在“用户界面”线程上显示结果,以便UI在发生数据收集时保持响应状态。在这种情况下,您有责任确保对集合的同步(“线程安全”)访问。通常使用简单的锁定机制或更复杂的同步机制(例如信号量,重置事件等)完成此操作。必须同步应用程序对集合的访问时,还必须确保来自WPF(特别是来自CollectionView)的访问参与其中以相同的同步机制。您可以通过调用EnableCollectionSynchronization方法来做到这一点。

DOC注释非常好,我认为您应该看一下: https://docs.microsoft.com/en-us/dotnet/api/system.windows.data.bindingoperations.enablecollectionsynchronization?view=netcore-3.1

答案 4 :(得分:-3)

当我遇到同样的问题时,我也无法弄清楚如何使用它。

我以自己的集合类型结束,我在那里存储调度程序并在必要时使用它。 请注意,我的命名非常差,这个集合不是线程安全的,远非它。

public class ThreadableObservableCollection<T> : ObservableCollection<T>
{
    private readonly Dispatcher _dispatcher;
    public ThreadableObservableCollection()
    {
      _dispatcher = Dispatcher.CurrentDispatcher;
    }

    public void ThreadsafeRemove(T item, Action callback)
    {
      if (_dispatcher.CheckAccess())
      {
        Remove(item);
        callback();
      }
      else
      {
        _dispatcher.Invoke(() =>
          {
            Remove(item);
            callback();
          });
      }
    }

    public void ThreadsafeInsert(int pos, T item, Action callback)
    {
      if (_dispatcher.CheckAccess())
      {
        Insert(pos, item);
        callback();
      }
      else
      {
        _dispatcher.Invoke(() =>
          {
            Insert(pos, item);
            callback();
          });
      }
    }
  }