C# 使用BindingOperations.EnableCollectionSynchronization
我有两个WPF应用程序“UI”、“Debugger”和一个类库“BL”。调试器和BL的UI引用。调试器对BL的引用。 我在BL有一个叫MyCollection的收藏。UI应用程序启动调试器应用程序,调试器绑定到BL中的集合MyCollection。当我尝试从UI应用程序更改MyCollection集合时,出现异常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
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);
}