WPF的MVVM框架Stylet开发文档 4. 视图模型优先ViewModel-first

4.ViewModel-first(视图模型优先)

原文链接ViewModel First

ViewModel-first 方法对 Stylet 的架构至关重要,但如果您以传统的 View-first 方式学习 MVVM,则不直观。

希望这篇文章能让一切都清楚。

视图优先的方法

让我们从定义视图优先的方法开始,我的意思是什么。MVVM 声明 ViewModel 应该对 View 一无所知,但 View 应该知道 ViewModel。那么,附加 View 和 ViewModel 的明显方法是让 View 在其代码隐藏中构造其 ViewModel - 如下所示:

public partial class MyView : Window
{
   public MyView()
   {
      InitializeComponent();
 
      this.DataContext = new MyViewModel();
   }
}

视图可以创建和拥有其他视图,这意味着您可以将视图组合成层次结构。

当你组合了几个视图时,关键就来了,比如这样说,其中一个外壳包含一个顶栏和一个框架,在其中可以显示任何页面:


<Window x:Class="MyNamespace.ShellView" ....>
   <StackPanel>
      <my:TopBarView/>
      <Frame x:Name="navigationFrame"/>
   StackPanel>
Window>

TopBarView有自己的视图模型TopBarViewModel…非常好。

现在假设TopBarView有一个字段包含您要更新的一些数据,例如当前页面的标题。现在,theShellViewModel知道这一点(毕竟它决定了当前页面是什么),但TopBarViewModel不知道(它会怎样?它什么都不知道)。诱惑是在TopBarView 上公开一个依赖属性并将其绑定到 中ShellViewModel,如下所示:

<Window x:Class="MyNamespace.ShellView" .... x:Name="rootObject">
   <StackPanel>
      <my:TopBarView CurrentPageTitle="{Binding CurrentPageTitle, ElementName=rootObject}"/>
      <Frame x:Name="navigationFrame"/>
   StackPanel>
Window>

但这很讨厌。您现在已经获得了绑定到ShellViewModel.

另一个主要问题是显示窗口和对话框。在传统的 MVVM 中,这有点痛苦。一种选择是从 ViewModel 内部实例化和显示视图(使用Show()或者 ShowDialog())(这使得它,或者至少它的那一点,不可测试)。更好的选择是在 View 的代码隐藏中实例化您想要显示的 View,并从那里显示它。这意味着您需要建立告诉 View 显示此对话框的方法,以及将对话框的结果返回给 ViewModel 的方法。

实际上,为Frame上述设置内容需要实例化一个 View 以放入其中。这有同样的困境——要么是 ViewModel 实例化它(使其无法测试),要么是 View 实例化它(导致沟通痛苦)。

无论哪种方式,这种方法都有一些讨厌的地方。

ViewModel-First 方法

ViewModel-first 方法接受 ViewModel 不应该知道任何关于其 View 的信息,但不接受 View 也应该负责构造 ViewModel。相反,第三个服务负责为给定的 ViewModel 定位正确的 View,并正确设置其 DataContext

默认实现使用命名约定为给定的 ViewModel 找到正确的 View,将其名称中的“ViewModel”替换为“View”。这在ViewManager中有更详细的解释。

这允许 ViewModel 由其他 ViewModel 创建。这允许 ViewModel 了解并拥有其他 ViewModel。这使您可以正确组合 ViewModel。

这个技巧还有另一部分,最好用例子来解释:

public class ShellViewModel
{
   public TopBarViewModel TopBar { get; private set; }
   // Stuff to instantiate and assign TopBarViewModel
}
<Window x:Class="MyNamespace.ShellView"
        xmlns:s="https://github.com/canton7/Stylet" .....>
   <StackPanel>
      <ContentControl s:View.Model="{Binding TopBar}"/>
      
   StackPanel>
Window>

View.Model附加属性将获取其绑定到(在这种情况下,它的一个实例的视图模型TopBarViewModel),并找到正确的观点(TopBarView)。它将实例化一个实例,并将其设置为那个的内容ContentControl

结果是TopBarView可以从其 中获取当前页面的名称TopBarViewModel,并且TopBarViewModel可以通过 告诉它ShellViewModel。问题解决了!

ContentControl技巧也适用于导航:

<Window x:Class="MyNamespace.ShellView"
        xmlns:s="https://github.com/canton7/Stylet" .....>
   <StackPanel>
      <ContentControl s:View.Model="{Binding TopBar}"/>
      <ContentControl s:View.Model="{Binding CurrentPage}"/>
   StackPanel>
Window>

然后ShellViewModel将通过实例化该页面的新实例ViewModel并将其分配给属性来导航到新页面CurrentPage。请注意如何ShellViewModel不再需要了解有关视图的任何信息。它不必实例化单个视图。这是一个非常重要、有用和强大的观点。

然后,将ShellViewModel通过实例化该页面的新实例ViewModel并将其分配给属性来导航到新页面CurrentPage。请注意如何ShellViewModel不再需要了解有关视图的任何信息。它不必实例化单个视图。这是一个非常重要、有用和强大的观点。

The WindowManager以几乎相同的方式处理对话框和窗口。这采用给定的 ViewModel 实例,并将其视图显示为对话框或窗口。

删除代码隐藏!

有了这种方法,您实际上不需要在代码隐藏中执行任何操作。您当然可以这样做,但是几乎没有什么是您无法通过Actions(用于处理事件)、转换器、附加属性和(最重要的)附加行为来解决的。

Stylet 允许您完全删除代码隐藏(它会为你调用InitializeComponent),我们强烈建议您这样做。删除代码隐藏!

注意*:如果您使用的是 VB.NET,有时您的 XAML 命名空间会在您删除代码隐藏后停止工作。如果是这种情况,只需使用匹配的文件名重新创建代码隐藏,为其提供正确的命名空间和类,然后将其余部分留空。例如MyView.xaml.vb:

Namespace Views
    Public Class MyView

    End Class
End Namespace

项目原地址:https://github.com/canton7/Stylet
当前文档原地址:https://github.com/canton7/Stylet/wiki/ViewModel-First

上一篇:WPF的MVVM框架Stylet开发文档 3. Bootstrapper引导程序
下一篇:WPF的MVVM框架Stylet开发文档 5. Actions

你可能感兴趣的:(MVVM,Stylet框架,wpf)