7

我已经阅读了几篇关于 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; }
}

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

4

5 回答 5

5

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

这就是你持有行为(验证、计算等)的地方。

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

这些是不同的,并且存在不同的原因 - 模式的重点是将您的显示逻辑分离到 VVM(View 和 ViewModel),并将您的业务逻辑完全分离。

于 2013-01-09T13:51:52.623 回答
5

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

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

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

上述注意事项:在模型中发生验证的情况并不少见,但即使这样,您也可能希望用户能够在编辑表单的过程中输入无效值。例如,如果您的模型不允许字段中的零长度值,您仍然希望让您的用户删除该值,移动到另一个字段(例如,复制它)然后返回到该字段并完成编辑(粘贴)。如果您直接绑定到模型,那么您的验证逻辑可能无法按照您的意愿处理这种“中间”、“尚未完成”状态。例如,您可能不希望在用户完成并单击“保存”之前向他们提出验证错误。

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

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

另一方面,模型通常尽可能纯净。理想情况下,您只需要(通常)对持久存储(数据库或您拥有的)中的内容进行精确建模的POCO 。如果您的持久存储具有 FirstName 和 LastName 字段,那么您的 Model 也将如此。只有在您的 ViewModel 中,您才会将它们组合起来以获得一个 Name 字段(“First Last”或“Last, First”,具体取决于 View 的需要)。

例如:

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 );
        }
    }
}
于 2013-01-09T13:52:57.867 回答
4

视图模型是您跟踪特定于视图而不是模型所必需的属性的地方。

让我们以您的模型为例,假设它被称为Person.

Person然后为被调用创建一个视图模型PersonViewModel,如下所示:

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

(注意,您可能不想直接像这样公开模型,但这是另一回事)

现在假设您在视图中有一个用于保存Person实例的按钮。为了提供更好的用户体验 (UX),您希望仅在您的模型实际发生更改时才启用该按钮。所以你在类上实现INotifyPropertyChanged接口Person

public class Person : INotifyPropertyChanged
{
    ...

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

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

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

    public bool HasUnsavedChanges { get; set; }
}

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

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

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

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

于 2013-01-09T14:02:09.300 回答
1

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

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

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

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

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

于 2013-01-09T13:59:25.283 回答
1

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

另一方面,视图模型是我在构建和运行应用程序时最终使用“构建块”(模型)的实际应用程序类。他们做一些事情,比如执行高级验证、处理命令、处理事件、任何类型的业务逻辑等。

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

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

但是,如果在某些情况下,视图中只需要模型中的一些属性,或者如果项目足够大,我希望将层完全分开,那么我通过 ViewModel 将模型的属性公开给视图就像您在示例代码中一样。

于 2013-01-09T14:06:15.933 回答