对于我的 WPF 应用程序,我决定使用 MVVM。这是我将如何实现这种模式的概念。
我的第一个问题是关于在 ViewModel 中包装或不包装我的模型。
ViewModel
出于各种原因应该包装模型:ViewModel
应该保护我的模型(我有一些属性,在表示层应该只有 getter 而没有 setter)我的决定是
ViewModel
绝对应该包装模型。所以ViewModel
工具INotifyPropertyChanged
.但是现在我在业务验证方面遇到了问题。
当我使用漂亮的 IDataErrorInfo 时,我在 ViewModel 中有整个业务规则,这打破了我的概念。业务规则绝对应该在模型中。
示例:当用户选择类型 A 时,则字段 1 和字段 2 为必填项。当用户选择类型 B 时,字段 3 是必填项——该字段应标记为红色,当它无效时,保存按钮将被禁用。还有更重的东西,比如空闲/占用的 DateTime-Ranges。
当我在 ViewModel 中做这些事情时,这绝对是糟糕的,因为大多数事情都是业务部分。
那么我如何才能做到这一点?
目前我有这个解决方法:
全部
ValidationRules
在模型中作为简单的方法,例如public string ValidateBirthday(string birthay)
{
if (...)
{
return "Birthday should be…";
}
return string.Empty;
}
在我的 ViewModel 中,我实现了 IDataErrorInfo,并像这样重定向到我的模型验证:
public string this[string columnName]
{
get
{
switch (columnName)
{
case "Birthday":
return Model.ValidateBirthday(Birthday);
case "XXX":
return Model.ValidateXXX(XXX);
case "YYY":
return Model.ValidateYYY(YYY);
break;
}
}
}
我从来没有在一个例子中看到这样的东西(重定向到模型),所以我对我的实现非常怀疑。
我的解决方法是否可行,或者您是否发现任何问题?
我试图提供更多关于我的意思的信息......
我知道模型中的实现 INotifyPropertyChanged 和 IDataErrorInfo。
这适用于从 View 到模型的直接绑定(bind)。
公共(public)类 PersonViewModel : INotifyPropertyChanged
{
私有(private)人_personModel;
公众人物人物模型
{
得到{返回_personModel; }
放
{
如果(_personModel != 值)
{
_personModel = 值;
NotifyPropertyChanged();
}
}
}
public PersonViewModel(Person person)
{
PersonModel = person;
}
…
}
看法:
<DatePicker Text="{Binding PersonModel.Birthday}"/>
最大的缺点是:WPF 控制所有强类型属性。
例子:
用户在日期选择器中输入 07/20/2008,因此将通知 PersonModel 并且 PersonModel 可以检查这一点,如果 OK,则 PersonModel 有效 => SaveButton 启用。
现在,用户在日期选择器中键入了“aaa”,WPF 将控制此验证,因为它绑定(bind)到强类型属性 (DateTime)。 PersonModel 不会被告知,因此 PersonModel 仍然有效 => SaveButton 已启用!
所以对于那个“问题”,我需要
ViewModel
正确。公共(public)类 PersonViewModel : INotifyPropertyChanged
{
私有(private)人_personModel;
public string Birthday
{
get
{
if (_personModel. Birthday!= null)
{
return ((DateTime) _personModel. Birthday).ToShortDateString();
}
else
{
return String.Empty;
}
}
set
{
if (_personModel. Birthday.ToString() != value)
{
DateTime dateValue;
if (DateTime.TryParse(value, out dateValue))
{
_personModel.Birthday = dateValue;
…
}
else
{
…
}
}
}
}
public PersonViewModel(Person person)
{
_personModel = person;
}
…
}
现在我不直接从 View 绑定(bind)模型。我从包装模型的 ViewModel 绑定(bind)属性。
<DatePicker Text="{Binding Birthday}"/>
最大的优势是:现在我可以完全控制用户在字段中键入的内容。
当用户在 Datepicker 中键入像 'aaa' 这样的字符串时,我可以捕捉到这个 => 将状态设置为无效并且 SaveButton 被禁用。
这就是为什么我不采用从 View 到 Model 的直接绑定(bind)的原因之一。
其他原因是 readonly 属性。在模型中,我在每个属性上都有 get 和 set,但为了安全问题,我不会使用 get 和 set 提供模型中的所有属性。因此,这也可以通过 ViewModel 通过仅使用 get 包装此属性来解决。使用直接绑定(bind)你不能做所有这些事情。
我的观点是,我肯定会将我模型中的所有属性都包装在 ViewModel 中,但是我该如何使用不错的
IDataErrorInfo
在模型中(它仅适用于直接绑定(bind))?
最佳答案
您在这里混合了两个概念:业务对象和验证。
现在几乎每个系统都使用客户端-服务器架构,即使它是一个独立的应用程序。
在这种情况下,您有两个验证位置:
还:
你的模型对象不应该有任何逻辑,因为你会用一些代码破坏它们,在某些时候你需要重用这些代码。
正如此处所公开的,您应该将该验证逻辑分成只知道该对象以及如何验证它们的服务。这样,您就可以从 UI 使用验证服务。
您的
Save
按钮应仅对 UI 更改使用react,并且您只能从 ViewModel 中获取这些更改。基本上,您将申请 SOLID principles这里:每一层都有非常明确的职责(模型 -> 数据,服务 -> 验证,dto -> 为客户端准备好数据, View 模型 -> UI 交互)。所有代码都将易于使用、易于扩展和易于重构。
编辑
第一和第二个问题 :
UI 只验证输入:数字字段中没有随机字符,文本字段中没有 sql 字符,日期格式正确等。
正如您所描述的那样,认为“如果这个那么那个”应该由后端处理:
第三个问题 :
这对我来说是正确的。
第四个问题 :
DTO 只是一个概念,你可以使用一个真正的后端服务器,通过 WCF 进行通信,或者你可以只拥有一堆充当服务但在同一个应用程序域中调用的类(就像任何其他项目引用一样)。无论哪种情况,您都可以选择发送和接收的数据。
你应该开始朝那个方向发展,然后看看什么更适合你。
关于WPF、MVVM、带有验证的业务对象不太匹配,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23490015/