MVVM中的模型是什么?

我已经阅读了几篇关于MVVM模式的文章,教程和博客文章。 但有一件事我不明白。 采取三个“层”:

  • 模型
  • 视图
  • 查看模型

据我所知,MVVM模型包含“原始”数据,例如Student类的名称和地址。 视图模型向视图公开属性,该属性表示模型的数据。

视图模型中的属性示例

 public string Name { get { return model.Name; } set { model.Name = value; } } 

模型示例

 private string name; public string Name { get { return name; } set { name = value; } } 

这可能听起来有点愚蠢,但这不会产生冗余吗? 为什么我必须在模型和视图模型中保留名称? 为什么不能完全处理视图模型上的名称?

在这样一个简单的例子中,这个答案是肯定的(这是不合理的冗余)。 但是,据推测,一个页面将包含的不仅仅是一个Model对象。 您可能拥有页面状态以及必须全部跟踪的多个其他Model对象。 这是在ViewModel中完成的。

例如,您可能有关于状态栏中显示的登录用户的其他信息,以及运行以检测文本文件更改的服务。

您可能还有一个用于编辑Student对象的表单。 如果您打算validation这些更改,那么在validation修改之前,您不希望直接编辑Student对象。 在这种情况下,ViewModel可以充当临时存储位置。

关于上述内容的注意事项:在模型中进行validation的情况并不少见,但即便如此,您可能希望用户在编辑表单的过程中能够输入无效值。 例如,如果您的模型不允许字段中的零长度值,您仍然希望允许您的用户删除该值,移动到另一个字段(例如,复制它)然后返回到字段和完成编辑(粘贴)。 如果您直接与模型绑定,那么您的validation逻辑可能无法处理此“中间”,“尚未完成”状态。 例如,您可能不希望在用户完成并点击“保存”之前将validation错误归咎于您的用户。

您也可能在ViewModel中有Command对象来处理按钮点击等。 这些将是特定于域的对象,在模型中无用。

当您需要过滤或以某种方式临时“修改”模型对象以在屏幕上获得有用的东西时,ViewModel也很有用。 例如,您可能希望显示系统中所有用户的列表以及其中前十名执行者的实时列表(每10秒更新一次)。 或者,您可能希望显示报告列表和显示总体使用率等的图表。过滤,排序和自定义数据将在ViewModel中进行。

另一方面,模型通常尽可能纯。 理想情况下,您只需要POCO (通常)模拟您的持久存储(数据库或您拥有的)中的确切内容。 如果您的持久存储具有FirstName和LastName字段,那么您的模型也是如此。 只有在ViewModel中才会将它们组合起来以获取名称字段(“最后一个”或“最后一个”,具体取决于视图的需要)。

例如:

 namespace Model { public class Student { public string FirstName { get; set; } public string LastName { get; set; } } public class Class { public string Name { get; set; } public float Score { get; set; } } } namespace ViewModel { public class EditStudentRecordViewModel { private Model.Student _student; private IEnumerable _studentClasses; /* Bind your View to these fields: */ public string FullName { return _student.LastName + ", " + _student.FirstName; } public string FirstName { get; set; } public string LastName { get; set; } public IEnumerable PassingClasses { get { return _studentClasses.Where( c => c.Score >= 78 ); } } public IEnumerable FailingClasses { get { return _studentClasses.Where( c => c.Score < 78 ); } } public void Save() { List l_validationErrors = new List(); if ( string.IsNullOrEmpty( this.FirstName ) ) l_validationErrors.Add( "First Name must not be empty." ); if ( string.IsNullOrEmpty( this.LastName ) ) l_validationErrors.Add( "Last Name must not be empty." ); if ( l_validationErrors.Any() ) return; _student.FirstName = this.FirstName; _student.LastName = this.LastName; Model.Utilities.SaveStudent( _student ); } } } 

该模型是包含业务逻辑的对象图。

这就是你持有行为的地方(validation,计算等)。

ViewModel是对UI及其交互进行建模的东西。

这些是不同的,并且有不同的原因 – 模式的要点是将显示逻辑与VVM(View和ViewModel)分开,并使您的业务逻辑完全分离。

您可以在视图模型中跟踪特定于视图的属性,而不是模型所必需的属性。

让我们拿你的模型,假设它叫做Person

然后为Person创建一个名为PersonViewModel的视图模型,如下所示:

 public class PersonViewModel { public Person Person { get; set; } } 

(注意,您可能不希望直接公开这样的模型,但这是另一个故事)

现在让我们假设您在视图中有一个用于保存Person实例的按钮。 为了提供更好的用户体验(UX),您只想在模型实际更改时启用该按钮。 因此,您在Person类上实现了INotifyPropertyChanged接口 :

 public class Person : INotifyPropertyChanged { ... 

现在,您可以Person公开HasUnsavedChanges属性,保存按钮上的Enabled属性将绑定到该属性,但该逻辑与此人无关

这是视图模型的用武之地。您可以在视图模型上定义此特定视图的属性 ,如下所示:

 public class PersonViewModel { public Person Person { get; set; } public bool HasUnsavedChanges { get; set; } } 

然后,您的视图模型将订阅INotifyPropertyChanged接口的PropertyChanged事件 ,并在视图模型上切换HasUnsavedChanges属性。

然后,如果绑定设置正确,则保存按钮将在模型上发生任何更改时启用/禁用,但您的模型没有任何逻辑将其绑定到视图。

请注意,您还必须在视图模型上实现INotifyPropertyChanged ,以便在对其绑定的视图模型进行更改时获取视图。

同样,这一点充当了包含逻辑的桥梁,逻辑是模型属性和不属于模型的视图属性的组合。

我一直将模型视为应用程序的“构建块”。 它们通常是具有某些属性的自包含类,并且可能仅对其自身属性进行一些基本validation或逻辑。

另一方面,View Models是我的实际应用程序类,在构建和运行应用程序时最终使用“构建块”(Models)。 他们执行诸如执行高级validation,处理命令,处理事件,任何类型的业务逻辑等操作。

应该注意的是,您不必像在示例代码中那样在ViewModel中公开Model的属性。 这样做是“MVVM纯粹主义”方法,因为它完全将Model层与View层分开,但是将整个Model公开给View也是完全可以接受的。 这是我在大多数小型项目中通常使用的,因为它简单且缺乏代码重复。

 public MyModel CurrentModel { get { return _model; } set { if (_model != value) { _model = value; RaisePropertyChanged("CurrentModel"); } } } 

但是,如果在View中只需要Model中的一些属性,或者项目足够大,我希望保持层完全分离,那么我通过ViewModel将我的Model的属性暴露给View就像你的示例代码中一样。

MVVM中的模型与MVP或Model2 MVC中的模型完全相同。 它是MVC启发模式的一部分,不受主题变化的影响。

Model是包含存储库,工作单元,域/模型对象,数据映射器,服务和一些其他结构的层。 它们组合在一起创建了模型层,其中包含特定应用程序的所有域业务逻辑。

模型不是任何单个实例。 任何向你倾诉的人都充满了它。

当您无法修改模型层或视图实例或两者时,设计MVVM的特定用例就是这种情况。

PS但是,如果您按照ASP.NET MVC文档使用ViewModel实例,那么您实际上并不使用MVVM。 它只是Model2 MVC,具有不同的名称(其中“viewmodels”实际上是视图,“views”是模板)。 当他们将类似Rails的架构作为“MVC”营销时,他们有点搞砸了。