在MVVM应用程序中切换ViewModel时出现BindingExpression路径错误

首先,一些背景。 如果您熟悉该问题,请跳至BindingExpression部分。 这是我在WPF中的第一个主要项目,所以我对MVVM模式还很陌生。 这是我发现的唯一一个类似的问题,其黯淡的回答并不能让我感到非常兴奋。

我/我正在构建一个.NET 3.5 WPF应用程序,我正在使用MVVM(自己实现,没有框架)。 在这里,我有许多ViewsViewModels 。 它们分别驻留在主ApplicationViewApplicationViewModel

我更改视图的方式是在ApplicationView使用XAML DataTemplate元素,如下所示:

    

然后在主体中我有一个ContentControl,它绑定到ApplicationViewModel的属性

  

当我运行应用程序时,所有这些似乎工作正常,并完全符合预期。 但是,当我在运行后查看Debug输出时,我得到了很多BindingExpression错误。

这是一个例子。 我的SplashText有一个属性InitViewModel 。 这绑定到初始屏幕( InitView )中的文本块。 当启动画面结束并切换视图模型时,我得到以下内容:

System.Windows.Data Error: 39 : BindingExpression path error: 'SplashText' property not found on 'object' ''MainMenuViewModel' (HashCode=680171)'. BindingExpression:Path=SplashText; DataItem='MainMenuViewModel' (HashCode=680171); target element is 'TextBox' (Name='FeedBackBox'); target property is 'Text' (type 'String')

我知道这是因为绑定仍然存在,但DataContext的CurrentPageViewModel属性已更改。 所以我想知道的是:

  • 这是一个短暂的问题,即在不使用时处理的视图还是它们(以及坏的绑定)无限期地存在于内存中?
  • 有没有办法在视图处于非活动状态时清理或停用这些绑定?
  • 如果我单独留下这些,会对我的申请产生什么样的表现?
  • 是否有更好的方法来切换视图以避免此问题?

在此先感谢,并为整体问题道歉。

编辑03/09/13 – 感谢Jehof,Francesco De Lisi和Faster Solutions指出将子视图datacontext设置为{Binding DataContext.CurrentPageViewModel, RelativeSource={RelativeSource AncestorType={x:Type Window}}}毫无意义因为ContentControl负责datacontext。

当您的属性属于另一个生成错误的ViewModel时,您的DataContext似乎会转到MainMenuViewModel

启动屏幕之前和之后的CurrentPageViewModel值在切换视图时更改失去其Binding

问题归结于DataContext="{Binding DataContext.CurrentPageViewModel, RelativeSource={RelativeSource AncestorType={x:Type Window}}}"

实际上,当你的应用程序启动时, CurrentPageViewModel = InitViewModel ,但问题是每个View都有相同的DataContext (首先是InitViewModel ),但我确信ViewModels没有满足视图绑定所需的整个所需属性池。 一个例子来理解:

ViewXPropertyX绑定,在ViewModelX管理。 ViewYPropertyY绑定,在ViewModelY管理。 两者都有DataContext = CurrentViewModel

在启动时, CurrentViewModel = ViewModelX ,ViewX和ViewY都有DataContext = ViewModelX 。 但这是错的 ! 并且可能会产生错误。

我通常做的是在View类中使用相应的View Model在DataContext(如果你愿意的话是cs或XAML)中设置它以确保它适合。 然后,在需要时,我每次切换页面时都会调用刷新方法来更新我的值。 如果您有共享属性,请考虑使用模型来集中您的信息(和值)。

来自http://wildermuth.com/images/mvvm_layout.png的示例图片

在此处输入图像描述

显然,视图是由MainWindow包装的控件。

希望很清楚。

您的具体示例在.NET 4.5中不可重现,这可能意味着Microsoft已经解决了此问题。

然而,当Content和ContentTemplate都是数据绑定时,存在类似的问题。 我将解决这个问题,如果有人还在使用它,它也可能解决.NET 3.5中的问题。 例如:

  

或者当DataTemplate由DataTrigger确定时:

      

在这两种情况下,都会得到类似于观察到的OP的绑定错误。

这里的技巧是确保以正确的顺序执行对Content和ContentTemplate的更改,以防止绑定错误。 我编写了DelayedContentControl ,它确保Content和ContentTemplate能够以正确的顺序同时更改。

  

类似地,对于DataTrigger案例。

您可以从我的opensource JungleControls库中获取DelayedContentControl。

让我们按顺序回答你的问题:

  1. 你可能已经知道了答案。 当.Net垃圾收集它时,它将从堆中删除您的View对象。 但是在此之前,您的View对象仍然绑定到页面上的主DataContext,并将对DataContext更改的事件做出反应。
  2. 显而易见的事情是将Views DataContext设置为null。 DataContext是一个依赖项属性,因此null值范围将只是您的View。
  3. 正如另一个/黯淡的回答所说的那样,它会减慢你的速度,但不是很多。 我不会太担心这个。
  4. 是。 这是视图导航选项的有用线程: 查看导航选项

我还建议看一个框架。 像MVVM Light这样轻量级的东西可以通过很少的集成为您解决一系列问题。 它的ViewModelLocator模式也可以做你正在做的事情,但没有副作用,并提供了一大堆清理选项。

您可以在视图中省略DataContext的绑定

 DataContext="{Binding DataContext.CurrentPageViewModel, RelativeSource={RelativeSource AncestorType={x:Type Window}}}" 

因为ViewDataContextContentControlDataContext ,它由Content -Property的绑定设置。

因此,当您的属性CurrentPageViewModel设置为InitViewModelContentControl将使用InitViewModel作为DataContext并使用InitView作为ContentTemplate ,它将自己的DataContext设置为InitView的DataContext。