为什么在WPF MVVM中要使用ContentControl + DataTemplate视图而不是直接使用XAML窗口视图?

88

为什么会这样?

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: (一个窗口而不是资源字典)

<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 对 View 完全一无所知,而在 VaW 中它(仅)需要在打开另一个窗口时能够实例化它
  • VaW:我可以在设计器中实际看到我的 XAML 渲染结果(至少在我的当前设置中VaD不能)
  • VaW:在打开和关闭窗口方面的工作方式很直观;每个窗口都有(即是)相应的 View(和 ViewModel)
  • VaD:ViewModel 可以通过属性传递初始窗口宽度、高度、可调整大小等参数(而在 VaW 中则是直接在 Window 中设置)
  • VaW:可以设置 FocusManager.FocusedElement(不确定在 VaD 中如何设置)
  • VaW:文件更少,因为我的窗口类型(例如 Ribbon、对话框)已纳入它们的 View 中

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

我很难理解为什么要将所有视图内容都打包到 ResourceDictionary 中。



2
像这样思考:ViewModel 应该显示在 Windows 或 UserControls 中。Poco 应该在 DataTemplates 中显示。 :) - dev hedgehog
3个回答

139

当人们想根据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

当然,如果只有一个预期的视图,并且从未更改,则这完全没有意义。


8

在VaD中,视图模型不知道任何关于视图的信息,因此您可以完全由视图模型构建一个完整的应用程序,而没有任何视图。这导致了编写完全由代码驱动的应用程序的可能性。这反过来又带来了在没有GUI的情况下执行集成测试的可能性。通过GUI进行集成测试是脆弱的,而通过视图模型进行测试应该更加健壮。


5
根据我的个人经验: 两种工作模式都可用,取决于您的需求和应用程序的要求。 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。后者可用于应用程序中不需要窗口的内部项目。根据应用程序要求和可用开发时间,选择最适合您的选项。希望这个人身经历能够帮到您...


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接