WPF MVVM 为啥使用 ContentControl + DataTemplate 视图而不是直接的 XAML 窗口视图?

Posted

技术标签:

【中文标题】WPF MVVM 为啥使用 ContentControl + DataTemplate 视图而不是直接的 XAML 窗口视图?【英文标题】:WPF MVVM Why use ContentControl + DataTemplate Views rather than straight XAML Window Views?WPF MVVM 为什么使用 ContentControl + DataTemplate 视图而不是直接的 XAML 窗口视图? 【发布时间】:2013-11-20 19:22:32 【问题描述】:

为什么会这样?

MainWindow.xaml:

<Window x:Class="MVVMProject.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <Grid>
        <ContentControl Content="Binding"/>
    </Grid>
</Window>

将您的 ExampleView.xaml 设置为:

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:vms="clr-namespace:MVVMProject.ViewModels">
    <DataTemplate DataType="x:Type vms:ExampleVM" >
        <Grid>
            <ActualContent/>
        </Grid>
    </DataTemplate>
</ResourceDictionary>

然后像这样创建窗口:

public partial class App : Application 

    protected override void OnStartup(StartupEventArgs e) 

        base.OnStartup(e);

        MainWindow app = new MainWindow();
        ExampleVM context = new ExampleVM();
        app.DataContext = context;
        app.Show();
    


什么时候可以这样做?

App.xaml:(设置启动窗口/视图)

<Application x:Class="MVVMProject.App"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    StartupUri="ExampleView.xaml">
</Application>

ExampleView.xaml:(一个窗口不是 ResourceDictionary)

<Window x:Class="MVVMProject.ExampleView"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:vms="clr-namespace:MVVMProject.ViewModels">
    >
    <Window.DataContext>
        <vms:ExampleVM />
    </Window.DataContext>

    <Grid>
        <ActualContent/>
    </Grid>
</Window>

本质上是“作为数据模板查看”(VaD)与“作为窗口查看”(VaW)

这是我对比较的理解:

VaD:允许您在不关闭窗口的情况下切换视图。 (这不适合我的项目) VaD:VM 对视图一无所知,而在 VaW 中,它(仅)必须能够在打开另一个窗口时实例化它 VaW:我实际上可以在 Designer 中看到我的 xaml(我不能 VaD,至少在我目前的设置中) VaW:直观地与 打开和关闭窗户;每个窗口都有(是)一个对应的视图 (和 ViewModel) VaD:ViewModel 可以通过属性传递初始窗口宽度、高度、可调整大小等(而在 VaW 中它们直接在窗口中设置) VaW:可以设置 FocusManager.FocusedElement(不确定在 VaD 中如何设置) VaW:文件更少,因为我的窗口类型(例如功能区、对话框)已合并到它们的视图中

那么这里发生了什么?我不能只在 XAML 中构建我的窗口,通过 VM 的属性干净地访问它们的数据,然后完成它吗?代码隐藏是相同的(几乎为零)。

我很难理解为什么我应该将所有 View 的东西都洗牌到 ResourceDictionary 中。

【问题讨论】:

这样想:ViewModel 应该显示在 Windows 或 UserControls 中。 Poco 应显示在 DataTemplates 中。 :) 【参考方案1】:

当人们想要根据 ViewModel 动态切换视图时,他们会使用 DataTemplates

<Window>
    <Window.Resources>
       <DataTemplate DataType="x:Type local:VM1">
          <!-- View 1 Here -->
       </DataTemplate>

       <DataTemplate DataType="x:Type local:VM2">
          <!-- View 2 here -->
       </DataTemplate>
    </Window.Resources>

    <ContentPresenter Content="Binding"/>

</Window>

所以,

如果Window.DataContextVM1的一个实例,那么会显示View1

如果

Window.DataContextVM2的一个实例,那么View2会被显示出来。

当然,如果只期望 1 个视图并且从未更改过,那根本没有意义。

【讨论】:

【参考方案2】:

由于在 VaD 中,视图模型对视图一无所知,因此您可以构建一个功能齐全的应用程序,完全由视图模型组成,而没有视图。这导致编写完全由代码驱动的应用程序的可能性。这反过来导致在没有 GUI 的情况下执行集成测试的可能性。通过 GUI 进行的集成测试是出了名的脆弱 - 而通过视图模型进行的测试应该更加健壮。

【讨论】:

【参考方案3】:

根据我的个人经验: 两种工作模式都是可行的,具体取决于您想要什么,并取决于应用程序要求。 VaD 背后的想法是解耦内容和容器。如果您实现VaD,您可以在显示任何此类项目时使用此模板(默认情况下)。您可以在ItemsControls(列表、列表视图、网格等)和ContentControls 中使用它,只进行绑定。就像你说的,VaD 用于在不关闭和打开新窗口的情况下切换窗口的内容。您还可以使用UserControls 定义视图,然后您可以控制焦点元素,还可以管理后面的代码。所以,你的数据模板可能是这样的:

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vms="clr-namespace:MVVMProject.ViewModels">
<DataTemplate DataType="x:Type vms:ExampleVM" >
    <CustomUserControl A="Binding A" B="Binding B" DataContext="Binding" .../>
</DataTemplate>

您还可以在UserControl 中设置依赖属性,这使工作更容易,因为允许绑定和解耦应用程序。

当然,如果您的应用不需要动态内容切换,则可以将VaW 用于主窗口或任何其他窗口。实际上,您可以同时使用VaWVaD。最后一个可用于应用程序中的内部项目,不需要窗口。您可以根据应用程序要求和开发应用程序的可用时间来选择更适合您的方法。 希望这种个人经历对您有所帮助...

【讨论】:

以上是关于WPF MVVM 为啥使用 ContentControl + DataTemplate 视图而不是直接的 XAML 窗口视图?的主要内容,如果未能解决你的问题,请参考以下文章

当我尝试使用 MVVM WPF 中的命令修改对象时,为啥在视图模型中出现空错误?

为啥要避免 WPF MVVM 模式中的代码隐藏?

MVVM WPF:为啥在运行应用程序时更新文本框中的文本后模型中的属性始终为空?

WPF MVVM

[WPF] 使用 MVVM Toolkit 构建 MVVM 程序

如何使用 MVVM 模式“禁用”WPF 中的按钮?