C# 使用BindingOperations.EnableCollectionSynchronization

C# 使用BindingOperations.EnableCollectionSynchronization,c#,wpf,multithreading,collections,observablecollection,C#,Wpf,Multithreading,Collections,Observablecollection,我有两个WPF应用程序“UI”、“Debugger”和一个类库“BL”。调试器和BL的UI引用。调试器对BL的引用。 我在BL有一个叫MyCollection的收藏。UI应用程序启动调试器应用程序,调试器绑定到BL中的集合MyCollection。当我尝试从UI应用程序更改MyCollection集合时,出现异常 A first chance exception of type 'System.NotSupportedException' occurred in PresentationFram

我有两个WPF应用程序“UI”、“Debugger”和一个类库“BL”。调试器和BL的UI引用。调试器对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.
我在谷歌上搜索发现: 我不知道怎么用它。我不想引用BL项目中的任何UI DLL。有人能帮我吗


谢谢你的帮助

当我遇到同样的问题时,我也不知道如何使用它

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

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();
          });
      }
    }
  }
公共类ThreadableObservableCollection:ObservableCollection
{
专用只读调度程序\u调度程序;
公共线程可观察集合()
{
_dispatcher=dispatcher.CurrentDispatcher;
}
public void ThreadsafeRemove(T项,操作回调)
{
if(_dispatcher.CheckAccess())
{
删除(项目);
回调();
}
其他的
{
_dispatcher.Invoke(()=>
{
删除(项目);
回调();
});
}
}
public void ThreadsafeInsert(int pos、T项、操作回调)
{
if(_dispatcher.CheckAccess())
{
插入(位置、项目);
回调();
}
其他的
{
_dispatcher.Invoke(()=>
{
插入(位置、项目);
回调();
});
}
}
}

我不确定这是否有用,但您还是可以试试

调试器
中添加一个
属性
,该属性将保存来自
BL的
集合
,如

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

public ObservableCollection<string> Data { get {return _data;} }
这将在生产线上方进行,以确保螺纹安全

下面是一个例子

视图模型(
调试器

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>


当窗口加载时,只需输入“SomeValue”在
组合框
中,然后按下
选项卡
键后,您应该会在
组合框
下拉列表中找到新值。在这篇文章中,您可以找到一个简单的教程,教您如何使用绑定操作……这非常简单。

我看到的所有关于堆栈溢出的示例都错了。从其他线程修改集合时,必须锁定集合

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


本文中的更多信息:

WPF应用程序可以使用ItemsControl或其子类(ListBox、DataGrid、TreeView、ListView等)显示数据集合。WPF通过CollectionView的子类传递对集合的所有访问。ItemsControl和CollectionView都与创建ItemsControl的线程有关联,这意味着禁止在不同的线程上使用它们并引发异常。实际上,此限制也适用于集合。您可能希望在多个线程上使用集合。例如,您希望在“数据收集”线程上更新集合(添加或删除项),同时在“用户界面”线程上显示结果,以便在进行数据收集时UI保持响应。在这种情况下,您负责确保对集合的同步(“线程安全”)访问。这通常使用简单的锁机制或更复杂的同步机制(如信号量、重置事件等)来完成。虽然必须同步应用程序对集合的访问,但还必须保证从WPF(特别是从CollectionView)进行的访问参与相同的同步机制。您可以通过调用EnableCollectionSynchronization方法来实现这一点

医生说得很好,我想你应该看看:

将NotifyProperty更改为ObservableCollection有什么意义?ObservableCollection为您做到了这一点。因此,您不需要将其用于可观察的收集。“BindingOperations.EnableCollectionSynchronization(_data,_lock);”不考虑线程安全。您仍然必须锁定_lock对象。当从另一个线程修改集合时,您必须锁定lock对象(您不必分派到UI线程来执行此操作)。有关更多信息,请参阅本文:@claudekennilol是正确的:您的代码必须围绕对observeCollection的任何访问执行
lock(_lock){…}
EnableCollectionSynchronization
所做的就是让WPF使用与您相同的锁锁定其代码。此示例代码(危险地)不完整。这没有使用问题(
BindingOperations.EnableCollectionSynchronization
)中询问的技术。我假设这将对一些集合更新事件进行排队,直到释放锁并且GUI可以处理它们,对吗?
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" };
    }
}
public partial class MainWindow : Window
{
    public MainWindow ()
    {
        InitializeComponent ();
        this.DataContext = new ViewModelClass ();
    }
}
<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>
_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);
}