2010-11-04 29 views
8

就我开发MVVM的WPF应用程序而言,我绝不会通过viewmodel的公共属性来暴露模型。无论如何,在我刚来到Silverlight和WCF RIA的世界之后,我发现了实现数据验证的新方法,这就是Required属性所说的。 (还有其他的属性)为什么在Silverlight MVVM中通过ViewModel公开模型并不好?

这次我不是在viewmodel里面创建验证逻辑,而是在模型本身内部完成几乎验证逻辑。

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

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

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

在那之后,我需要在视图模型是暴露TestUserPM的类型的公共财产,让查看直接绑定到模型。

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

这种方法有什么缺点吗?

更新1

我刚刚发现1下侧,可它有解决方案在那里。我想绑定Button的命令,例如,按钮保存到ViewModel中的Command,但是当且仅当所有信息都有效时才能执行此按钮。从我使用WPF MVVM的经验来看,我将帮助OnCanExecuteChanged()public string this[string columnName]之内IDataErrorInfo

我该如何处理这种要求?

回答

10

我通过ViewModel一直暴露模型,只是为了保持简单,不要重复自己(DRY)。

避免需要在模型中添加属性以适应用户界面(如Benjamin注意到的)的唯一方法是将模型保留为viewModel的样式,因此您可以将属性添加到viewModel中,而无需搞乱模型。

即:该视图模型是DataContext的,它有一个型号的财产返还模型

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

我也这么做。 ViewModel在这里公开和调整模型到视图。 – 2010-11-04 16:24:35

+0

使用该模型的想法直接违反了封装规则,因为没有人应该执行'MyObject.Child.ChildChild.NChild.SomeProperty'。它也是模型和视图之间的分隔,因为如果你在你的模型中改变了Propertyname,你必须在你的视图/ – WiiMaxx 2015-12-13 17:49:58

4

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

你可以想象在相同的对象上具有不同验证级别的几个UI。你的例子是不可能的。

1

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

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

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

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

+0

我刚刚从文件读取它们,我不明白,为什么我们需要直接从服务注入的ViewModel样?这是与UI相关的任务,它必须位于UI级别,而不是服务。 (在我看来)基本上这意味着我需要把所有东西都放在服务中,对我来说很陌生。 :) – Anonymous 2010-11-04 11:57:45

+0

@In粉红色:对不起,我不明白你的问题(你认为哪种服务是RIA服务?)。 ViewModel是UI,但它公开的数据模型是从Web服务填充的业务对象。使用RIA服务验证可以在客户端和服务端进行验证。 – 2010-11-04 12:17:18

1

为了揭露模型视图模型,你需要准备你的模型,以适应浏览,因此,你应该污染模型有观点具体代码:

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

有必要不污染与其他的事情你的模型,完美的模型应该与其他图书馆小相关,因此它可以在不同的平台(asp.net,手机,单相同的应用程序中共享, winform,wpf等)。或者升级/降级。

反正..

我做了一个small WPF application(尚未完成),我用uNhAddins,NHibernate的,城堡内置它。我不认为这是最好的解决方案,但我真的很乐意与它合作。首先查看代码,然后看看实体,验证逻辑,业务逻辑的分离。程序集分离的设计是为了尽量减少核心应用程序,UI和应用程序逻辑之间的依赖关系。

4

问题是因为其他人说你无法适应这种观点。 然而,我经常不想重复自己 - 就像爱德华多在模型曝光时所说的那样。 我发现解决方案有点不一致,当你想改变视图的值 - 那么一些将绑定“Model.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); 
} 

}

+0

中做到这一点。性能如何?它似乎会减慢它一点...不是? – 2011-01-08 23:19:17

+0

表现很好。当然有一个小的开销,但反射电话并不昂贵。如果您有很多属性,则可以使用字典查找来交换数组查找。 – 2011-01-10 08:28:16

相关问题