【问题标题】:BindingExpression path errors when switching ViewModels in MVVM application在 MVVM 应用程序中切换 ViewModel 时出现 BindingExpression 路径错误
【发布时间】:2013-09-02 06:08:53
【问题描述】:

首先,一些上下文。如果您熟悉该问题,请跳至BindingExpression 部分。这是我在 WPF 中的第一个主要项目,所以我对 MVVM 模式还是很陌生。 Here 是我发现的唯一一个类似的问题,其平淡无奇的回答并没有让我很兴奋。

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

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

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

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

<ContentControl Content="{Binding CurrentPageViewModel}"/>

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

这是一个例子。我的InitViewModel 中有一个属性SplashText。这绑定到初始屏幕中的文本块 (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 指出将子视图数据上下文设置为 {Binding DataContext.CurrentPageViewModel, RelativeSource={RelativeSource AncestorType={x:Type Window}}} 是没有意义的,因为 ContentControl 负责处理数据上下文。

【问题讨论】:

    标签: c# wpf mvvm .net-3.5


    【解决方案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。

    【讨论】:

    • 我知道这已经很老了,但我只是遇到了同样的问题,并尝试使用这个解决方案。虽然 DelayedContentControl 完成了它的编码,但它实际上非常慢(我在 TabControl 中有这个 bindingexpression 问题,所以“更改选项卡”的简单行为需要几秒钟)你有没有遇到过任何其他可能的解决方案在这种情况下会有所帮助吗?
    【解决方案2】:

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

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

    因为您的ViewDataContextContentControlDataContext,并且由您绑定Content-Property 设置。

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

    【讨论】:

    • 有用,谢谢,但不能解决绑定表达式路径错误
    【解决方案3】:

    看起来您的 DataContext 转到 MainMenuViewModel 而您的属性属于另一个 ViewModel 生成错误。

    初始屏幕更改前后的CurrentPageViewModel 值在切换视图时丢失其Binding

    问题出在DataContext="{Binding DataContext.CurrentPageViewModel, RelativeSource={RelativeSource AncestorType={x:Type Window}}}"

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

    ViewX 绑定到PropertyX,在ViewModelX 中管理。 ViewY 绑定到PropertyY,在ViewModelY 中管理。 两者都有DataContext = CurrentViewModel

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

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

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

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

    希望清楚。

    【讨论】:

    • 那么您的建议是将数据上下文直接硬编码到每个视图中,而不是绑定到主视图模型中的 currentViewModel 属性?对我来说,这似乎有点混乱。但是,我认为这个答案可能是迄今为止最接近实际提供解决方案的 3 个答案。
    • 1:1 绑定是最简单的解决方案。查看 msdn.microsoft.com/en-us/library/gg405494(v=pandp.40).aspx 了解高级 MVVM 模式。
    • 按1:1绑定,你的意思是Mode=OneTime吗?
    【解决方案4】:

    让我们依次回答你的问题:

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

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

    【讨论】:

    • 但是当视图不再在范围内时将 datacontext 设置为 null 会停止 bindingexpression 路径错误吗?在用户控件的特定事件中,您将如何决定何时取消数据上下文?
    • 我想会的。你看,DataContext 是一个依赖属性。因为它是一个依赖属性,它会做几件事:它会通知绑定的变化,它会保存一个值的层次结构。通过拥有自己的值,它将不再依赖于从您的页面/用户控件/窗口继承的值。至于决定何时取消上下文,这取决于您的应用程序结构。一种选择是监视子视图上的 DataContextChanged 事件。这会告诉您 DataContext 何时更改,然后您可以对该事件做出反应。
    • 这一切都很好,并且可以解决从InitViewMainMenuView 的转换,因为我们不必重新访问启动屏幕。但是,问题来了,例如,我从主菜单切换到工作流视图模型,然后又回到主菜单。然后它的 datacontext 将为空,我需要找到一种方法来重置它,或者只是接受绑定表达式错误。
    • 对,所以你有一组共享单个 DataContext 绑定的单例视图和视图模型。这意味着,如果您持有对每个视图的引用,您将从每个已打开但不再可见的视图中获取绑定表达式错误 - 因此绑定表达式错误的数量将会增加用户在浏览应用程序时。您最好的选择是使用基于页面的导航系统(谷歌“基于页面的导航 wpf”)。 WPF 提供了你需要的所有脚手架,这将解决这个问题。
    • 这是非常错误的。当 ContentTemplate 发生变化时,所有先前生成的控件都会销毁 立即。绑定在此之前不久停止工作。 GC 在这里没有任何作用。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-05-14
    • 2022-01-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-08-11
    相关资源
    最近更新 更多