在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线程中创建的,这只会产生更多的问题。

在MVVM中绑定WPF线程时访问错误

好吧,我花了好长时间才找到问题的根本原因。

与其他人怀疑的不同,这根本不是多线程问题。
相反,这是您正在使用的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=ExampleCollectionLineChart.ItemsSourceProperty上设置的Binding无法找到任何源值,而只是将null分配给ItemsSource。这是因为DataContext是从父级继承的,但是当没有父级时,也就没有DataContext可以继承。
因为Chart不是可视或逻辑树的一部分,所以没有(简单的)方法可以绑定到外部的DataContext

要验证DataContextnull,只需将这行添加到前面的代码中:

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(或指定的)线程上运行更改。