就我用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]
。
我如何处理这种要求?
答案 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和应用程序逻辑之间的依赖关系。