题 在MVVM中,每个ViewModel是否只与一个模型耦合?


在MVVM实现中,是每一个 ViewModel 只有一个 Model

我试图在项目中实现MVVM模式,但我发现有时,a View 可能需要多个信息 Models

例如,对于a UserProfileView, 它的 UserProfileViewModel 可能需要来自的信息 UserAccountModelUserProfileSettingsModelUserPostsDataModel

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

怎么会 ViewModel 当它必须从多个获取信息时工作 Models?或者MVVM中会出现这种情况吗?

PS:我没有使用Prism或Unity Framework。我正在尝试将类似的模式实现到我正在使用的不使用Prism或Unity的项目中。这就是为什么我需要准确理解其中一些是如何工作的。


33
2017-10-26 11:00


起源


是什么让你认为ViewModel不能绑定到不同的数据源? - Akku
View仅通过依赖注入包含一个模型。因此构造函数只接受一个Model。 - Carven
请重读我的评论:是什么让你认为ViewModel(不是视图)不能绑定到不同的其他模型(不是ViewModels) - Akku
是的,我指的是ViewModel,而不是View。错字错误。对不起。 - Carven
ViewModel是一个View of Model,因此它只与View直接相关。不要试图遵守规则,模式和实践中没有严格的规则。 - Alex Burtsev


答案:


在我对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
  }
}

38
2017-10-26 11:18



谢谢!这个例子真的有助于澄清这个想法。 +1 :) - Carven


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

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


14
2017-10-26 11:07



谢谢!但是如何将多个模型注入ViewModel?从UML图中,我观察到通常,ViewModel只有一个参数,它接受一个Model。 - Carven
取决于你使用什么。任何IoC都可以注入多个模型。 Caliburn.Micro(我最喜欢的MVVM框架)也是如此 - jgauffin
嗯......我不明白当ViewModel在其构造函数中只有一个View参数时,IoC如何将多个模型注入ViewModel?我正在研究类似的结构: i.imgur.com/ZKEDz.png - Carven
@xEnOn:你的图表令人困惑。通常,ViewModel不依赖于正在使用它的View。它取决于ViewModel的View,并将其作为构造函数参数。 IView抽象的要点是,如果你想访问一个 不同 在ViewModel中查看,您可以在不破坏封装的情况下执行此操作。 - Alex J
@xEnOn:我认为通过Model,你的意思是某种对象可以让你从程序的其他部分获得信息(例如:域模型服务)。没有限制你应该只依赖其中一个,并且在ViewModel类中有多个构造函数参数是完全没问题的。例如,您可以在ViewModel中存储对这些内容的引用,以供以后在readonly字段中使用。 - Alex J


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

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

编辑:为什么你认为:

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

EDIT2:

有两种主要的方法可以使用mvvm,首先是“View First”,第二种是“Viewmodel First”,你当然可以将它们混合起来并根据需要选择最佳方法。


4
2017-10-26 11:31





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

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

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


2
2017-10-26 11:12





我会确保您了解view,viewmodel和所有其他模型类之间的区别。 ViewModel是模型对象,其中填充了视图可以绑定到的数据。它只是为视图提供数据,这使得ViewModel对象可以单元测试,并且整个业务逻辑与视图分离。因此,您可以完全开发业务逻辑而无需使用视图本身,并且可以仅使用构建或使用其他视图并绑定到ViewModel对象的属性来替换视图。例如,如果视图中包含空文本字段,则文本字段的内容可以绑定到视图模型的不同属性。

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

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


2
2017-10-26 11:13





通常每个模型有一个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


1
2017-10-26 11:05



是吧?您可能需要重新编写更清晰的内容。这听起来像你说View应该有多个ViewModel给我。 - Andrew Grothe
“每个视图都拥有自己的ViewModel”是不可理解的?对不起我的语言能力差,但我完全同意你的看法。一个视图仅与一个视图模型相关联。但是这个ViewModel本身可以引用其他几个ViewModel。 - PVitt
@agrothe:我试图澄清ViewModels的用法。对不起,我的语言能力差...... - PVitt
更多的是“回答你的问题,ViewModel1引用ViewModelA和ViewModelB”这一行令人困惑。该代码有助于澄清您想说的内容。 - Andrew Grothe


只需在视图中使用用户模型即可

public partial class User : Login
{
    public string Password { get; set; }

    public List<Customer> customer { get; set; }
}

在这个继承的另一个模型登录和该模型中也使用的客户模型..


-2
2018-06-17 09:35



切勿在视图中使用模型! MVVM正是关于这个设计缺陷,并希望用ViewModel概念来解决它。 - PVitt