2013-08-29 63 views
6

首先要做的事情是,先设置一些上下文。如果您熟悉问题,请跳至BindingExpression部分。这是我在WPF中的第一个主要项目,所以我对MVVM模式仍然很陌生。 Here是我发现的唯一另外一个类似的问题,它的低调答案并没有真正引起我很大的兴趣。在MVVM应用程序中切换ViewModels时发生BindingExpression路径错误

我有/建立一个.NET 3.5 WPF应用程序,我使用MVVM(自己实现,没有框架)。在此范围内,我有一些ViewsViewModels。这些分别位于主人ApplicationViewApplicationViewModel之内。

我更改视图的方式是通过使用XAML的DataTemplate元件在ApplicationView,像这样:

<DataTemplate DataType="{x:Type viewmodels:InitViewModel}"> 
    <views:InitView /> 
</DataTemplate> 

然后在主体我有结合于场所在ApplicationViewModel

<ContentControl Content="{Binding CurrentPageViewModel}"/> 
一个ContentControl中

当我运行该应用程序时,所有这些似乎工作正常,并且完全按照预期操作。但是,当我在运行后查看调试输出时,出现很多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。

回答

1

看起来你的DataContext转到MainMenuViewModel,而你的财产属于另一个ViewModel产生错误。

CurrentPageViewModel值前后闪屏切换失败Binding切换视图。

问题是dued到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类中设置DataContext(如果您愿意,可以使用cs或XAML),并使用相应的View Model来确保它合适。然后,在需要时,每次切换页面时我都会调用刷新方法来更新我的值。如果您有共享属性,请考虑使用模型集中您的信息(和值)。

样本图像从http://wildermuth.com/images/mvvm_layout.png

enter image description here

显然的意见是由你的主窗口包裹的控件。

希望它很清楚。

+0

那么你所建议的是直接将datacontext硬编码到每个视图中,而不是绑定到主视图模型中的currentViewModel属性?对我来说,这似乎有点混乱。然而,我认为这个答案可能是迄今为止实际提供解决方案中最接近的3个答案。 –

+0

1:1绑定是最简单的解决方案。查看http://msdn.microsoft.com/en-us/library/gg405494(v=pandp.40).aspx高级MVVM模式。 –

+0

by 1:1 binding,你是说Mode = OneTime? –

0

让我们回答您的问题顺序:

  1. 你或许已经知道这个问题的答案。当.NET垃圾收集时,它会从堆中移除你的View对象。但是直到这个时候,你的View对象仍然绑定到页面上的主DataContext,并且会对DataContext已更改的事件作出反应。
  2. 显而易见的事情是将Views DataContext设置为null。 DataContext是一个依赖项属性,因此空值范围将只是您的View。
  3. 正如其他/暗淡的答案所说,它会减慢你一点,但不是很多。我不会太担心这件事。
  4. 是的。这里有一个有用的线程查看导航选项:View Navigation Options

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

+0

但是当视图不再在范围内时,将datacontext设置为null停止bindingexpression路径错误?你将如何决定何时取消datacontext,在usercontrol的特定事件? –

+0

我认为会的。你看,DataContext是一个依赖属性。因为它是一个依赖属性,它会做几件事情:它会通知绑定的变化,并且它会保存一个值的层次结构。通过拥有自己的价值,它将不再取决于它从Page/User Control/Window中继承的价值。至于决定何时取消上下文,这取决于您的应用程序结构。一种选择是监视你的子View上的DataContextChanged事件。这会告诉你什么时候DataContext发生了变化,然后你可以对该事件做出反应。 –

+0

这一切都很好,并且会修复从'InitView'到'MainMenuView'的过渡,因为我们不必重新访问启动画面。但是,例如,当我切换到主菜单的工作流视图模型,然后返回到主菜单时,就会出现问题。它的datacontext将是空的,我需要找到一种方法来重置它,或者只是接受绑定表达式的错误。 –

0

可以省略的DataContext的结合在你的浏览

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

原因您ViewDataContextContentControlDataContext和得到你的Content,物业结合设置。

所以,当你的财产CurrentPageViewModel设置为InitViewModelContentControl将使用InitViewModelDataContext,并使用InitViewContentTemplate,它会设置自己的DataContext作为InitView的DataContext的。

+0

有用,谢谢,但不解决bindingexpression路径错误 –

1

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

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

<ContentControl Content="{Binding Content}" ContentTemplate="{Binding Template}" /> 

或者当的ContentTemplate由DataTrigger确定:

<ContentControl Content="{Binding Content}"> 
    <ContentControl.Style> 
     <Style TargetType="{x:Type ContentControl}"> 
      <Style.Triggers> 
       <DataTrigger Binding="{Binding Choice}" Value="1"> 
        <Setter Property="ContentTemplate" Value="{StaticResource TemplateA}" /> 
       </DataTrigger> 
       <DataTrigger Binding="{Binding Choice}" Value="2"> 
        <Setter Property="ContentTemplate" Value="{StaticResource TemplateB}" /> 
       </DataTrigger> 
      </Style.Triggers> 
     </Style> 
    </ContentControl.Style> 
</ContentControl> 

在两种情况下,可以得到类似的观察到的那些OP绑定错误。

这里的窍门是确保Content和ContentTemplate的更改以正确的顺序执行以防止绑定错误。我写了DelayedContentControl,它确保Content和ContentTemplate同时按照正确的顺序进行更改。

<jc:DelayedContentControl Content="{Binding Content}" ContentTemplate="{Binding Template}"> 

对于DataTrigger情况也是类似的。

您可以从我的开源JungleControls library获得DelayedContentControl。

相关问题