为什么在Silverlight MVVM中通过ViewModel公开Model是不好的?

时间:2010-11-04 11:31:08

标签: silverlight mvvm

就我用MVVM开发WPF应用程序而言,我从未通过viewmodel的公共属性公开模型。无论如何,在我刚刚来到Silverlight和WCF RIA的世界之后,我找到了实现数据验证的新方法,这就是必需属性所说的。 (还有其他属性)

这次不是在viewmodel中创建验证逻辑,而是在模型本身内部几乎可以做验证逻辑。

public class TestUserPM {
    [Key]
    public int ID { get; set; }

    [Required]
    public string FirstName { get; set; }

    [Required]
    public string Email { get; set; }
}

之后,我在ViewModel中需要的只是暴露TestUserPM类型的公共属性,并让View绑定直接到模型。

我认为这不是优雅的解决方案,但它可以工作,并且无需在viewmodel属性中创建繁琐的验证。

这种方法有没有缺点?

更新1

我刚刚找到了1个下方,可能是它有解决方案。我想绑定Button的Command,例如,按钮保存到ViewModel中的Command,但是当且仅当所有信息都有效时,此按钮才能执行。根据我对WPF MVVM的经验,我帮助上课,我会在OnCanExecuteChanged()的{​​{1}}内调用public string this[string columnName]

我如何处理这种要求?

5 个答案:

答案 0 :(得分:10)

我一直通过ViewModel公开Model,只是为了让事情变得简单而不重复(DRY)。

唯一要避免在模型中添加属性以适应UI(如Benjamin所说)的方法是将模型保持为viewModel的属性,这样就可以向viewModel添加属性,而不会弄乱模特。

ie:ViewModel是DataContext,它有一个Model属性返回模型

<TextBlock Text={Binding Path=Model.Name} />
<TextBlock Text={Binding Path=Model.Address} />

答案 1 :(得分:5)

我看到的主要问题是您的模型(可能是业务对象)必须适应UI。它可能会影响很多其他UI或业务层。

您可以想象在同一对象上有多个具有不同验证级别的UI。您的示例无法做到这一点。

答案 2 :(得分:4)

问题在于其他人已经说过你无法适应这种观点。 然而,我经常不想重复自己 - 正如Eduardo也说过要接触模型的曝光。 当你想要为视图改变一个值时,我发现这个解决方案有点不一致 - 然后有些人会绑定“Model.Name”而其他人只是改变了属性的“Name” - 而且有些场景不会这样工作。

我的解决方案是创建一个ViewModelProxy类,您可以在其中转发另一个类的属性并免费获取属性通知。通过派生DynamicObject(我遗漏了代码通知,IDataerror等)很容易完成。很酷的是,Data的所有属性都被转发 - 如果你实现/覆盖一个将被绑定的属性 - 这样你就不必重复代码了,你有一个明智的做法就是使用DynamicObject。

public class ViewModelProxy<T> : DynamicObject, INotifyPropertyChanged

public T Data { get; set; }

private PropertyInfo[] objectProperties;
private PropertyInfo[] ObjectProperties
{
  get
  {
    if (objectProperties == null)
      objectProperties = typeof(T).GetProperties(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);
    return objectProperties;
  }
}
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
  var pinfo = ObjectProperties.FirstOrDefault((pi) => pi.Name == binder.Name);

  if (pinfo != null)
  {
    result = Data != null ? pinfo.GetValue(Data, null) : null;
    return true;
  }
  else
    return base.TryGetMember(binder, out result);
}


public override bool TrySetMember(SetMemberBinder binder, object value)
{
  var pinfo = ObjectProperties.FirstOrDefault((pi) => pi.Name == binder.Name);

  if (pinfo != null)
  {
    if (Data != null)
      pinfo.SetValue(Data, value, null);
    RaisePropertyChanged(binder.Name);
    return true;
  }
  else
    return base.TrySetMember(binder, value);
}

}

答案 3 :(得分:1)

通过Silverlight中的注释使用验证是正确的,而不是使用代码填充ViewModel。

在任何特殊验证规则的情况下,您可以创建自定义验证器并使用[CustomValidation ...]装饰成员,这将再次使验证远离ViewModel。

在任何情况下,您描述的业务规则通常在视图之间共享。对于特殊情况视图,可以在控制器中添加特定验证。

作为一般观点:ViewModel是一个相对愚蠢的对象,用于保存视图的值。如果你开始发现你正在添加逻辑,事件处理程序和其他,你应该看看引入一个控制器对象......即使MVVM中没有C:)

答案 4 :(得分:1)

要在ViewModel中公开Model,您需要准备模型以适应视图,因此您应该使用视图特定代码污染模型:

  • 通知物业变更支持
  • 数据错误信息支持(内部验证)
  • 可编辑支持。

有必要不要用其他东西污染你的模型,完美的模型应该与其他库有最小的依赖关系,因此它可以在不同平台的同一个应用程序中共享(asp.net,mobile,mono,winform,wpf等等)。或升级/降级。

反正..

我做了small WPF application(尚未完成),我使用了uNhAddins,NHibernate,Castle来构建它。我不是说这是最好的解决方案,但我很高兴能与它一起工作。首先查看代码,然后看看实体,验证逻辑,业务逻辑的分离。设计程序集分离是为了最大限度地减少核心应用程序,UI和应用程序逻辑之间的依赖关系。