代码之家  ›  专栏  ›  技术社区  ›  David

为什么在SilverlightMVVM中通过ViewModel公开模型不好?

  •  8
  • David  · 技术社区  · 14 年前

    就我用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类型的公共属性,并让视图直接绑定到模型。

    我认为这不是一个优雅的解决方案,但它可以工作,并且不需要在ViewModel属性中创建冗长的验证。

    这种方法有什么缺点吗?

    更新1

    我刚在下面找到了一个,可能有解决办法。我想要绑定按钮的命令,例如,在viewmodel中,button save to命令,但是只有当所有信息都有效时,这个按钮才能执行。根据我对wpf mvvm的经验,我将调用helper类 OnCanExecuteChanged() 里面 public string this[string columnName] 属于 IDataErrorInfo .

    我如何处理这种要求?

    5 回复  |  直到 14 年前
        1
  •  10
  •   Eduardo Molteni    14 年前

    我一直在通过ViewModel公开模型,只是为了保持简单,不要重复我自己(干)。

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

    ie:viewModel是DataContext,它有一个返回模型的模型属性

    <TextBlock Text={Binding Path=Model.Name} />
    <TextBlock Text={Binding Path=Model.Address} />
    
        2
  •  5
  •   Benjamin Baumann    14 年前

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

    您可以想象在同一个对象上具有不同验证级别的几个UI。这在您的示例中是不可能的。

        3
  •  4
  •   Rune Andersen    14 年前

    问题是,正如其他人所说,你不能适应这种观点。 然而,我常常不想重复我自己-正如爱德华多也说过的,暴露在模特身上是要约束的。 我发现,当您想更改视图的值时,这个解决方案有点不一致,那么有些方案将绑定“model.name”,而其他方案则只是更改属性的“name”,而有些方案就不能这样工作。

    我的解决方案是创建一个viewModelProxy类,在该类中可以转发其他类的属性,并免费获取属性通知。通过派生dynamicObject(我省略了代码通知、IDataError等)很容易完成。很酷的是,数据中的所有属性都会被转发——如果您实现/重写一个将绑定到的属性——因此,这样就不必重复代码,并且您有一个明智的方法来使用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);
    }
    

    }

        4
  •  1
  •   iCollect.it Ltd    14 年前

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

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

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

    一般来说:视图模型是一个相对哑的对象,用于保存视图的值。如果您开始发现您正在添加逻辑、事件处理程序和其他,那么您可能应该考虑引入一个控制器对象…即使MVVM中没有C:)

        5
  •  1
  •   ktutnik    14 年前

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

    • 通知属性更改支持
    • 数据错误信息支持(内部验证)
    • 可编辑支持。

    不要用其他东西污染你的模型,完美的模型应该与其他库的依赖性最小,这样它就可以在同一个应用程序中与不同的平台(ASP.NET、Mobile、Mono、WinForm、WPF等)共享。或升级/降级。

    反正…

    我做了一个 small WPF application (还没有完成),我用了unhadins,nhibernate,castle来建造它。我不认为这是最好的解决方案,但我真的很高兴与之合作。首先检查代码,然后查看实体、验证逻辑和业务逻辑的分离。程序集分离的设计是为了最小化核心应用程序、UI和应用程序逻辑之间的依赖关系。