7

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

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

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

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

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

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

当我运行该应用程序时,所有这些似乎都工作正常,并且完全符合预期。但是,当我在运行后查看 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 指出设置子视图数据上下文是没有意义的,{Binding DataContext.CurrentPageViewModel, RelativeSource={RelativeSource AncestorType={x:Type Window}}}因为 ContentControl 负责处理数据上下文。

4

4 回答 4

3

您的具体示例在 .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 库中获取 DelayedContentControl 。

于 2014-07-18T21:12:08.560 回答
1

看起来您DataContext去了,MainMenuViewModel而您的财产属于另一个ViewModel产生错误的财产。

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

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

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

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

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

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

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

在此处输入图像描述

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

希望很清楚。

于 2013-09-03T10:02:38.933 回答
0

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

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

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

于 2013-09-03T10:05:02.880 回答
0

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

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

因为DataContext你的View是你的DataContext,并且由你的-PropertyContentControl绑定设置。Content

所以,当你的属性CurrentPageViewModel设置为一个时InitViewModelContentControl将使用InitViewModelasDataContext并使用InitViewasContentTemplate并将他自己的 DataContext 设置为 InitView 的 DataContext。

于 2013-09-03T11:00:53.803 回答