【问题标题】:In MVVM, is every ViewModel coupled to just one Model?在 MVVM 中,每个 ViewModel 是否都只与一个 Model 耦合?
【发布时间】:2012-10-16 15:28:45
【问题描述】:

在 MVVM 实现中,每个 ViewModel 是否仅与一个 Model 耦合?

我正在尝试在项目中实现 MVVM 模式,但我发现有时,View 可能需要来自多个Models 的信息。

例如,对于UserProfileView,其UserProfileViewModel 可能需要来自UserAccountModelUserProfileSettingsModelUserPostsDataModel 等的信息。

但是,在我阅读的大多数关于 MVVM 的文章中,ViewModel 仅包含一个通过依赖注入的模型。所以构造函数只接受一个模型。

ViewModel 必须从多个Models 获取信息时如何工作?或者在 MVVM 中会不会出现这种情况?

PS:我没有使用 Prism 或 Unity 框架。我正在尝试将类似的模式实施到我正在从事的不使用 Prism 或 Unity 的项目中。这就是为什么我需要确切地了解其中一些是如何工作的。

【问题讨论】:

  • 是什么让您认为 ViewModel 不能绑定到不同的数据源?
  • 视图通过依赖注入仅包含在一个模型上。所以构造函数只接受一个模型。
  • 请重读我的评论:是什么让您认为 ViewModel(不是视图)不能绑定到不同的其他模型(不是 ViewModels)
  • 是的,我指的是 ViewModel,而不是 View。错字。很抱歉。
  • ViewModel 是 View 的 Model,所以只和 View 直接相关。不要试图遵守规则,模式和实践中没有严格的规则。

标签: c# design-patterns mvvm


【解决方案1】:

通常每个模型都有一个 ViewModel。这些 ViewModel 包含处理模型数据的逻辑。另一方面,每个视图也都有自己的视图模型。所以这意味着:

class ModelA 
{
    bool TestValue{get;set;}
}
class ViewModelA<ModelA>
{
    ValueViewModel<bool> TestValue{get; private set;}

    public ViewModelA(ModelA model) 
    {
        base.Model = model;
        this.Initialize();
    }
}

class ModelB 
{
    string Username;
}
class ViewModelB<ModelB>
{
    ValueViewModel<string> Username{get; private set;}

    public ViewModelB(ModelB model) 
    {
        base.Model = model;
        this.Initialize();
    }
}

这些是封装模型的 ViewModel。视图有自己的 ViewModel:

public ViewModelForExactlyOneView
{
    public ViewModelA{get;set;}
    public ViewModelB{get;set;}
}

为了回答您的问题,ViewModel1 指的是 ViewModelA 和 ViewModelB。因此 View 可以从 ViewModel1.ViewModelA.TestValue 获取它的数据。

【讨论】:

  • 嗯?您可能希望重新编写它以使其更加清晰。听起来你说一个视图应该有多个视图模型对我来说。
  • “每个视图都有自己的视图模型”是不是不能理解?对不起,我的语言能力不好,但我完全同意你的看法。一个视图恰好链接到一个视图模型。但是这个 ViewModel 本身可以引用其他几个 ViewModel。
  • @agrothe:我试图阐明 ViewModel 的使用。对不起,我的语言能力不好......
  • 更令人困惑的是“为了回答您的问题,ViewModel1 指的是 ViewModelA 和 ViewModelB”这一行。该代码有助于澄清您要说的内容。
【解决方案2】:

您可以在一个视图模型中使用多个模型。视图模型的目的是抽象出业务/数据层(即模型)。

但是,使用多个模型通常表示视图太大。您可能希望将其拆分为用户控件(它们有自己的视图模型)。

【讨论】:

  • 谢谢!但是如何将多个模型注入到 ViewModel 中?从 UML 图中,我观察到通常,ViewModel 只有一个参数,可以接受单个模型。
  • 取决于你使用什么。任何 IoC 都可以注入多个模型。 Caliburn.Micro(我最喜欢的 MVVM 框架)也是如此
  • hmm...我不明白当 ViewModel 在其构造函数中只有一个 View 参数时,IoC 如何能够将多个模型注入 ViewModel?我正在查看类似以下内容的结构:i.imgur.com/ZKEDz.png
  • @xEnOn:您的图表令人困惑。通常,ViewModel 不依赖于使用它的 View。 View 依赖于 ViewModel,并将其作为构造函数参数。 IView 抽象的要点是,如果您想访问 ViewModel 中的不同视图,您可以在不破坏封装的情况下这样做。
  • @xEnOn:我假设模型是指某种对象,它可以让您从程序的其他部分(例如:域模型服务)获取信息。没有任何限制,您应该只依赖其中之一,并且在您的 ViewModel 类中拥有多个构造函数参数是完全可以的。您可以在 ViewModel 中存储对这些的引用以供以后使用,例如,在只读字段中。
【解决方案3】:

一个 ViewModel 可能并且在许多情况下确实使用多个模型。它本身就是您视图的“模型”。

考虑一个用户输入其个人信息(包括地址)的个人资料屏幕。如果地址存储在“addresses”表中,其余存储在“profile”表中,则 ViewModel 使用 Profile 和 Address 模型来创建统一的 ViewModel。

正如jgauffin 在他的回答中提到的那样,很多时候您可以使用用户控件来实现一对一的关系,但您也可以通过尝试 100% 的时间来引入不必要的复杂性。

【讨论】:

    【解决方案4】:

    我会确保您了解视图、视图模型和所有其他模型类之间的区别。 ViewModel 是填充了视图可以绑定到的数据的模型对象。它的存在只是为了向视图提供数据,这使得 ViewModel 对象可进行单元测试,并且整个业务逻辑与视图分离。因此,您可以在不使用视图本身的情况下完全开发业务逻辑,并且可以通过构建或使用另一个视图并绑定到 ViewModel 对象的属性来替换视图。例如,如果一个视图充满了空文本字段,则文本字段的内容可以绑定到视图模型的不同属性。

    通常真的应该只有一个视图模型。但如果太复杂,您可以使用绑定对象的子属性,如Binding to ViewModel.SubClass.Property (sub-property)中所述

    ViewModel 可以从许多不同的来源、业务对象、数据库等获取它返回给视图的数据。

    【讨论】:

      【解决方案5】:

      在我对 MVVM 模式的理解中,唯一实际的要求是 View 从 ViewModel 的属性中获取其所有数据(可能通过绑定机制)。 ViewModel 是您专门为该视图制作的一个类,它负责根据需要自行填充。你可以把它想象成视图的 ActiveRecord。

      因此,您在 ViewModel 中执行什么操作来获取其属性应显示的数据并不重要。您可以通过查询一些服务、读取一个或多个业务实体模型、在现场生成它或以上所有方式来获取它。需要结合所有这些东西来制作功能视图是完全正常的。

      与任何演示模式一样,关键是将在屏幕上显示某些数据的过程与获取该数据的过程分开。这样您就可以分别测试流程的每个部分。

      编辑:这是一个小但希望完整的依赖流示例。

      // Model/service layer
      
      public class MyModelA
      {
        public string GetSomeData()
        {
          return "Some Data";
        }
      }
      
      public class MyModelB
      {
        public string GetOtherData()
        {
          return "Other Data";
        }
      }
      
      // Presentation layer
      
      public class MyViewModel
      {
        readonly MyModelA modelA;
        readonly MyModelB modelB;
      
        public MyViewModel(MyModelA modelA, MyModelB modelB)
        {
          this.modelA = modelA;
          this.modelB = modelB;
        }
      
        public string TextBox1Value { get; set; } 
      
        public string TextBox2Value { get; set; }
      
        public void Load()
        {
          // These need not necessarily be populated this way. 
          // You could load an entity and have your properties read data directly from it.
          this.TextBox1Value = modelA.GetSomeData();
          this.TextBox2Value = modelB.GetOtherData();
          // raise INotifyPropertyChanged events here
        }
      }
      
      public class MyView
      {
        readonly MyViewModel vm;
      
        public MyView(MyViewModel vm)
        {
          this.vm = vm;
          // bind to vm here
        }
      }
      
      // Application layer
      
      public class Program
      {
        public void Run()
        {
          var mA = new MyModelA();
          var mB = new MyModelB();
          var vm = new MyViewModel(mA, mB);
          var view = new MyView(vm);
          vm.Load();
          // show view here
        }
      }
      

      【讨论】:

      • 谢谢!这个例子真的有助于澄清这个想法。 +1 :)
      • 这很好,但是当 ModelA 和 ModelB 类中的数据发生变化时如何通知 MyViewModel 类呢?如何将 MyViewModel 类绑定到 ModelA 和 ModelB 类变量?
      • 通常,您的模型仅在 UI 要求更改时才会更改。您可能会在某处单击“保存”按钮,然后您可以使用它来刷新数据。然而,有时这并不容易做到。如果您的模型通过从外部服务获取数据而自行更改,或者如果它被程序的其他部分修改,那么最简单的方法是在您的模型/业务层中公开一个事件,并监听该事件在 ViewModel 中。
      • 请务必在窗口关闭时取消订阅,否则窗口将绑定到模型的生命周期。最好的方法是在 ViewModel 上实现 IDisposable,并在关闭时处理它,或者更好的是,让您的依赖注入容器处理它。
      【解决方案6】:

      视图模型包含“视图逻辑” - 因此您想在视图上显示的所有内容都通过视图模型公开。如果您想显示来自不同“模型”的数据,那么您的视图模型会将其聚合起来,并且视图可以绑定到。

      mvvm 的主要目的是 btw 单元测试。这意味着无需 UI 即可轻松测试视图逻辑。

      编辑:你为什么认为:

      ViewModel 在其构造函数中只有一个 View 参数

      EDIT2:

      顺便说一句,使用 mvvm 有两种主要方法,第一种是“View First”,第二种是“Viewmodel First”,您当然可以将两者混合使用,然后选择最适合您需要的方法。

      【讨论】:

        【解决方案7】:

        只需在您的视图中使用 User 模型

        public partial class User : Login
        {
            public string Password { get; set; }
        
            public List<Customer> customer { get; set; }
        }
        

        在此模型中继承了另一个模型登录,并且此模型中也使用了客户模型..

        【讨论】:

        • 永远不要在视图中使用模型! MVVM 正是针对这个设计缺陷,并希望通过 ViewModel 概念来解决它。
        猜你喜欢
        • 2011-08-02
        • 2017-11-06
        • 1970-01-01
        • 2010-10-20
        • 1970-01-01
        • 2017-12-06
        • 2013-06-14
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多