MVVM中的模型是用来做什么的
本文关键字:什么 模型 MVVM | 更新日期: 2023-09-27 17:51:03
我读了几篇关于MVVM模式的文章、教程和博客文章。但是有一件事我不明白。选取三个"图层":
- <
- 视图/gh>
就我所理解的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对象的表单。如果您打算验证这些更改,那么在验证修改之前,您不会希望直接编辑Student对象。在这种情况下,ViewModel可以充当临时存储位置。
上面的注意:在模型中发生验证并不罕见,但即使这样,您也可能希望用户能够在编辑表单的过程中输入无效的值。例如,如果您的模型不允许字段中的零长度值,您仍然希望允许用户删除该值,移动到另一个字段(例如,复制它),然后返回到该字段并完成编辑(粘贴)。如果您直接绑定到模型,那么您的验证逻辑可能无法像您希望的那样处理这种"中间"、"尚未完成"的状态。例如,在用户完成验证并点击"保存"之前,你可能不想让用户发现验证错误。
您可能还会在ViewModel中使用Command对象来处理按钮单击等操作。这些将是特定于领域的对象,在模型中是无用的。
当你需要过滤或临时"修改"模型对象以在屏幕上获得有用的东西时,ViewModels也很有用。例如,您可能希望显示系统中所有用户的列表,以及其中表现最好的10个用户的实时列表(每10秒更新一次)。或者,您可能希望显示报表列表和显示总体使用率的图表等。筛选、排序和自定义数据将在ViewModel中进行。 另一方面,模型通常是尽可能纯净的。理想情况下,您只希望poco(通常)准确地模拟持久存储(数据库或其他存储)中的内容。如果您的持久存储有FirstName和LastName字段,那么您的Model也有。只有在你的ViewModel中,你才能将它们组合起来获得一个Name字段(根据视图的需要,要么是"First Last",要么是"Last, First")。 例如:
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<Model.Class> _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<Model.Class> PassingClasses
{
get
{
return _studentClasses.Where( c => c.Score >= 78 );
}
}
public IEnumerable<Model.Class> FailingClasses
{
get
{
return _studentClasses.Where( c => c.Score < 78 );
}
}
public void Save()
{
List<string> l_validationErrors = new List<string>();
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 );
}
}
}
模型是包含业务逻辑的对象图。
这是保存行为(验证,计算等)的地方。
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
,以便您的视图在对绑定到的视图模型进行更改时拾取。
再一次,这个点作为一个桥梁来包含逻辑,这个逻辑是模型属性和不属于模型的视图属性的组合。
MVVM中的模型与MVP或Model2 MVC中的模型完全相同。这是mvc样式中唯一不受主题变化影响的部分。
模型是包含存储库、工作单元、领域/模型对象、数据映射器、服务和一些其他结构的层。所有这些组合起来创建了模型层,其中包含特定应用程序的所有域业务逻辑。Model不是任何单个实例。任何跟你说不一样的人都是胡说八道。
MVVM设计的特定用例是当您无法修改模型层或视图实例,或两者都无法修改时的情况。
<子> 注:但是,如果您使用的是
ViewModel
实例。. NET MVC文档,那么你实际上没有使用MVVM。它只是具有不同名称的Model2 MVC(其中"视图模型"实际上是视图,"视图"是模板)。当他们把类似rails的架构称为"MVC"时,他们搞砸了。
我一直将模型视为应用程序的"构建块"。它们通常是自包含的类,具有一些属性,可能仅针对其自身的属性进行一些基本的验证或逻辑。
另一方面,视图模型是在构建和运行应用程序时最终使用"构建块"(模型)的实际应用程序类。它们执行高级验证、处理命令、处理事件、任何类型的业务逻辑等。 应该注意的是,你没有让在你的ViewModel中暴露你的模型的属性,就像你在你的示例代码中那样。这样做是"MVVM纯粹主义者"的方法,因为它完全将你的模型层从视图层中分离出来,然而,将整个模型公开给视图也是完全可以接受的。这是我通常在大多数小项目中使用的方法,因为它简单且缺乏代码重复。public MyModel CurrentModel
{
get { return _model; }
set
{
if (_model != value)
{
_model = value;
RaisePropertyChanged("CurrentModel");
}
}
}
但是,如果在视图中只需要模型的几个属性,或者如果项目足够大,我想要保持图层完全分开,那么我通过ViewModel将模型的属性暴露给视图,就像您在示例代码中所做的那样。