这篇MSDN文章是否违反了MVVM?

ras*_*asx 4 silverlight wcf entity-framework mvvm

这可能是旧闻,但早在2009年3月,本文" Silverlight 2 Apps中的Model-View-ViewModel "就有了一个代码示例,其中包括DataServiceEntityBase:

// COPIED FROM SILVERLIGHTCONTRIB Project for simplicity

/// <summary>
/// Base class for DataService Data Contract classes to implement 
/// base functionality that is needed like INotifyPropertyChanged.  
/// Add the base class in the partial class to add the implementation.
/// </summary>
public abstract class DataServiceEntityBase : INotifyPropertyChanged
{
/// <summary>
/// The handler for the registrants of the interface's event 
/// </summary>
PropertyChangedEventHandler _propertyChangedHandler;

/// <summary>
/// Allow inheritors to fire the event more simply.
/// </summary>
/// <param name="propertyName"></param>
protected void FirePropertyChanged(string propertyName)
{
  if (_propertyChangedHandler != null)
  {
    _propertyChangedHandler(this, new PropertyChangedEventArgs(propertyName));
  }
}

#region INotifyPropertyChanged Members
/// <summary>
/// The interface used to notify changes on the entity.
/// </summary>
event PropertyChangedEventHandler INotifyPropertyChanged.PropertyChanged
{
  add
  {
    _propertyChangedHandler += value;
  }
  remove
  {
    _propertyChangedHandler -= value;
  }
}
#endregion
Run Code Online (Sandbox Code Playgroud)

这个类意味着开发人员打算将视觉直接绑定到数据(是的,使用ViewModel但它定义了一个ObservableCollection数据对象).这种设计是否偏离了MVVM的指导?现在我可以看到一些原因为什么我们会采用这种方式:我们可以做的DataServiceEntityBase是这种事情(与实体框架密切相关):

// Partial Method to support the INotifyPropertyChanged interface
public partial class Game : DataServiceEntityBase
{
    #region Partial Method INotifyPropertyChanged Implementation
    // Override the Changed partial methods to implement the 
    // INotifyPropertyChanged interface

    // This helps with the Model implementation to be a mostly
    // DataBound implementation

    partial void OnDeveloperChanged() { base.FirePropertyChanged("Developer"); }
    partial void OnGenreChanged() { base.FirePropertyChanged("Genre"); }
    partial void OnListPriceChanged() { base.FirePropertyChanged("ListPrice"); }
    partial void OnListPriceCurrencyChanged() { base.FirePropertyChanged("ListPriceCurrency"); }
    partial void OnPlayerInfoChanged() { base.FirePropertyChanged("PlayerInfo"); }
    partial void OnProductDescriptionChanged() { base.FirePropertyChanged("ProductDescription"); }
    partial void OnProductIDChanged() { base.FirePropertyChanged("ProductID"); }
    partial void OnProductImageUrlChanged() { base.FirePropertyChanged("ProductImageUrl"); }
    partial void OnProductNameChanged() { base.FirePropertyChanged("ProductName"); }
    partial void OnProductTypeIDChanged() { base.FirePropertyChanged("ProductTypeID"); }
    partial void OnPublisherChanged() { base.FirePropertyChanged("Publisher"); }
    partial void OnRatingChanged() { base.FirePropertyChanged("Rating"); }
    partial void OnRatingUrlChanged() { base.FirePropertyChanged("RatingUrl"); }
    partial void OnReleaseDateChanged() { base.FirePropertyChanged("ReleaseDate"); }
    partial void OnSystemNameChanged() { base.FirePropertyChanged("SystemName"); }
    #endregion
}
Run Code Online (Sandbox Code Playgroud)

当然,MSDN代码可以被视为用于教育目的的"玩具代码",但是在Silverlight开发的现实世界中是否有人在做这样的事情?

Ant*_*nes 6

为了使View完全独立于Model,您需要重现类型,这些类型在很多情况下与ViewModel中的Model类型相同.

模型包含Person具有FirstNameLastName属性的类型.视觉设计要求一个"人名单",所以包含有一个数据模板绑定的属性路径的列表框视图FirstNameLastName.在ItemsSource结合于暴露的是有一个类型的一组实例视图模型的属性FirstNameLastName特性.

所以这里有一个问题,如果Model Person类型的"ViewModel版本" 或者ViewModel是否应该只重用Person模型中的现有类型?

在任何一种情况下,你很可能希望这些属性是可观察的.

要考虑

MVVM背后的目标是什么?我们常常喜欢提供一个很好的长列表,列出为什么一个模式存在,但在这种情况下,实际上只有2个.

  • 从代码中分离出视觉设计(注意:不设计).
  • 最大化整个应用程序的可测试表面.

在ViewModel上公开模型类型不会对上述任何一个目标构成障碍.事实上,它有助于测试,因为减少了需要测试的类型和成员的数量.

在我看来,我没有看到实现INotifyPropertyChanged 意味着绑定到视觉效果.某些服务可能希望观察模型对象属性的变化可能还有其他原因.

模型与视图分离的关键原则是隐藏有关视图如何从模型本身呈现模型的任何细节.向ForenameBackColor模型添加属性可能会很糟糕.这就是ViewModel的用武之地.

底线

要求模型公开可观察属性并不违反MVVM,它是一个简单而通用的要求,不要求模型具有任何View的任何特定知识,或者确实存在任何"视觉".