在MVVM中绑定WPF线程时访问错误
本文关键字:访问 错误 线程 WPF MVVM 绑定 | 更新日期: 2023-09-27 18:17:54
所以我试图将WPF的动态数据显示合并到我的MVVM校准项目中(我正在使用未来动态数据显示的LineChart控件,如果有人想测试此错误)。有一个LineChart绑定到一个ObservableCollection。它仅在具有LineChart的控件的代码跟踪中进行收集时才有效。如果您尝试绑定到ViewModel的集合,则依赖属性会引发InvalidOperationException。如何解决这个问题?
我见过当你改变属性绑定到的集合时发生这种情况,并且知道如何修复它,但从来没有在实际的绑定过程中发生过。我已经尝试将集合的创建放在调度程序调用中(就像您使用添加或删除一样),但它没有帮助。
编辑:正如我在第二段中所说的,例外不是在改变集合的时候。它在绑定点被提出来。更重要的是,我已经尝试了其他问题的解决方案,但它们没有帮助。
编辑#2:异常消息是"调用线程不能访问这个对象,因为另一个线程拥有它"。人们一直在告诉我收集更改的解决方案,但它甚至没有进行更改。它在绑定阶段失败(ItemsSource="{binding collection}"在xaml中)。
编辑#3:我仔细检查并注意到ViewModel是在UI线程中创建的,这只会产生更多的问题。
好吧,我花了好长时间才找到问题的根本原因。
与其他人怀疑的不同,这根本不是多线程问题。
相反,这是您正在使用的DynamicDataDisplay
库的问题。
有一个明确的原因,为什么你的ItemsSource
绑定工作在你的ListBox
对象,不工作在Chart
(类型为Microsoft.Research.DynamicDataDisplay.Markers2.LineChart
):Chart
既没有可视父元素,也没有逻辑父元素。
如果您将以下代码插入Button_Click
并在它们之后设置断点,则可以检查此情况:
var visualParent = VisualTreeHelper.GetParent(Chart);
var logicalParent1 = Chart.Parent;
var logicalParent2 = LogicalTreeHelper.GetParent(Chart);
你可以看到它们都是null
。
因此,您使用Path=ExampleCollection
在LineChart.ItemsSourceProperty
上设置的Binding
无法找到任何源值,而只是将null
分配给ItemsSource
。这是因为DataContext
是从父级继承的,但是当没有父级时,也就没有DataContext
可以继承。
因为Chart
不是可视或逻辑树的一部分,所以没有(简单的)方法可以绑定到外部的DataContext
。
要验证DataContext
是null
,只需将这行添加到前面的代码中:
var dataContext = Chart.DataContext;
现在有三种可能的解决方案。
首先,您可以使用以下代码手动从Window
继承DataContext
:
private void Window_Loaded(object sender, RoutedEventArgs e)
{
// Just add the following line.
Chart.DataContext = DataContext;
Chart.StrokeThickness = 3;
Chart.SetBinding(LineChart.ItemsSourceProperty, new Binding("ExampleCollection"));
// ...
}
如果你只是添加这一行,你会看到你的其他多线程代码工作得很好,图表更新为某种正弦波模式。
第二个,作为另一个可能的解决方案,您可以参考DynamicDataDisplay
库的文档,并检查使用数据绑定将ItemsSource
分配给LineChart
的正确和预期方法。
我试着自己搜索文档,甚至花了两个小时左右的时间从那个库中调试了很多代码,但文档几乎不存在,代码太复杂了,无法在几个小时内完全理解。我尝试使用几个工具(Visual Studio Live Visual Tree, Snoop,…)来显示ChartPlotter
的视觉树,但我每次都得到StackOverflowException
,所以基本上这个库中的某些东西是有缺陷的和有缺陷的。
第三个,你可以使用Resource
作为一种代理对象来创建一个"绑定桥"到MainWindowViewModel
的相同实例。要做到这一点,您必须执行下面建议的操作:在可视化树之外进行数据绑定。数据上下文桥接
底线:因此,如果您只想完成工作,我会像上面所示那样在代码中设置DataContext
。(特别是如果DataContext
中的ViewModel
的实例从未改变。)
如果您想使用纯数据绑定,那么我可能会使用"绑定桥"或搜索支持此场景的其他图表库。
正如THIS线程所述,您可以使用UI分派器并调用在UI线程上更改ObservableCollection的函数。application。current。dispatcher应该给你UI dispatcher。正如解决方案所建议的那样,它可以正确地在ViewModel中。然而,一个更简洁、更通用的解决方案是实现一个并发的、仍然可观察的集合,它仍然会存储调度程序,并在UI(或指定的)线程上运行更改。