我已经阅读了几篇关于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; }
}
这可能听起来有点愚蠢,但这不会产生冗余吗?为什么我必须在模型和视图模型中保留名称?为什么不能完全处理视图模型上的名称?
答案 0 :(得分:5)
模型是包含业务逻辑的对象图。
这就是你保持行为的地方(验证,计算等)。
ViewModel可以模拟UI及其交互。
这些是不同的,并且有不同的原因 - 模式的要点是将显示逻辑与VVM(View和ViewModel)分开,并使您的业务逻辑完全分离。
答案 1 :(得分:5)
在这样一个简单的例子中,这个答案是肯定的(这是不合理的冗余)。但是,据推测,一个页面将包含的不仅仅是一个Model对象。您可能具有页面状态以及必须全部跟踪的多个其他Model对象。这是在ViewModel中完成的。
例如,您可能有关于状态栏中显示的登录用户的其他信息,以及运行以检测文本文件更改的服务。
您可能还有一个用于编辑Student对象的表单。如果您打算验证这些更改,那么在验证修改之前,您不希望直接编辑Student对象。在这种情况下,ViewModel可以充当临时存储位置。
关于上述内容的注意事项:在模型中进行验证的情况并不少见,但即使这样,您也可能希望用户在编辑过程中能够输入无效值。形成。例如,如果模型在字段中不允许零长度值,您仍然希望允许用户删除该值,移动到另一个字段(例如,复制它)然后返回到字段,完成编辑(粘贴)。如果您直接与模型绑定,那么您的验证逻辑可能无法处理您所希望的“中间”,“尚未完成”状态。例如,您可能不希望在用户完成并点击“保存”之前将验证错误归咎于您的用户。
您也可能在ViewModel中使用Command对象来处理按钮点击等。这些将是特定于域的对象,在模型中无用。
当您需要过滤或以某种方式临时“修改”模型对象以在屏幕上获得有用的东西时,ViewModel也很有用。例如,您可能希望显示系统中所有用户的列表以及其中前十名执行者的实时列表(每10秒更新一次)。或者您可能希望显示报告列表和显示总体使用率等的图表。过滤,排序和自定义数据将在ViewModel中进行。
另一方面,模型通常尽可能纯净。理想情况下,您只需要POCOs(通常)模型确切地说明持久存储中的内容(数据库或您拥有的内容)。如果您的持久存储具有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<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 );
}
}
}
答案 2 :(得分:4)
您可以在视图模型中跟踪特定于视图的属性,而不是模型所需的属性。
让我们选择你的模型,假设它被称为Person
。
然后为Person
创建一个名为PersonViewModel
的视图模型,如下所示:
public class PersonViewModel
{
public Person Person { get; set; }
}
(注意,您可能不希望直接公开这样的模型,但那是另一个故事)
现在让我们假设您在视图中有一个用于保存Person
实例的按钮。为了提供更好的用户体验(UX),您只想在模型实际更改时启用该按钮。因此,您要在Person
类上实施INotifyPropertyChanged
interface:
public class Person : INotifyPropertyChanged
{
...
现在,你可以从HasUnsavedChanges
公开Person
属性,保存按钮上的Enabled
属性将绑定到该属性,但该逻辑有<没有与这个人有关。
这是视图模型的用武之地。您可以在视图模型上定义此视图特定属性,如下所示:
public class PersonViewModel
{
public Person Person { get; set; }
public bool HasUnsavedChanges { get; set; }
}
然后,您的视图模型将订阅INotifyPropertyChanged
接口的PropertyChanged
event,并在视图模型上切换HasUnsavedChanges
属性。
然后,如果绑定设置正确,当模型上发生任何更改时,保存按钮将启用/禁用,但您的模型没有任何逻辑将其绑定到视图。
请注意,您还必须在视图模型上实现INotifyPropertyChanged
,以便在对其绑定的视图模型进行更改时获取视图。
同样,这一点充当了包含逻辑的桥梁,逻辑是模型属性和不属于模型的视图属性的组合。
答案 3 :(得分:1)
我一直将模型视为应用程序的“构建块”。它们通常是具有某些属性的自包含类,并且可能仅对其自己的属性进行一些基本验证或逻辑。
View模型另一方面是我的实际应用程序类,在构建和运行应用程序时最终使用“构建块”(模型)。他们执行的操作包括执行高级验证,处理命令,处理事件,任何类型的业务逻辑等。
应该注意的是,您没有拥有来在ViewModel中公开Model的属性,就像在示例代码中一样。这样做是“MVVM纯粹主义”方法,因为它将您的模型层与视图层完全分开,但是将整个模型公开给视图也是完全可以接受的。这是我在大多数小型项目中通常使用的,因为它简单且缺乏代码重复。
public MyModel CurrentModel
{
get { return _model; }
set
{
if (_model != value)
{
_model = value;
RaisePropertyChanged("CurrentModel");
}
}
}
但是,如果在View中只需要Model中的一些属性,或者项目足够大,我希望保持图层完全分离,那么我将Model的属性暴露给View像你在示例代码中一样通过ViewModel。
答案 4 :(得分:0)
MVVM中的模型与MVP或Model2 MVC中的模型完全相同。它是受MVC启发的模式的一部分,不受主题变化的影响。
模型是包含存储库,工作单元,域/模型对象,数据映射器,服务和一些其他结构的层。它们组合在一起创建了模型层,其中包含特定应用程序的所有域业务逻辑。
模型不是任何单个实例。任何向您推销的人都充满了它。
当您无法修改模型层或视图实例或两者时,设计MVVM的特定用例就是这种情况。
P.S。但是,如果您根据ASP.NET MVC文档使用
ViewModel
实例,那么您实际上并不使用MVVM。它只是Model2 MVC,具有不同的名称(其中“viewmodels”实际上是视图,“views”是模板)。当他们将类似Rails的架构作为“MVC”营销时,他们有点搞砸了。