我有一个使用MVVM模式的应用程序,我想在用户填写信息时实现验证。
我想使用IDataErrorInfo,但我不知道我的视图模型是否实现了该接口,或者我创建新类是否更好。使用IDataErrorInfo和MVVM模式实现验证的最佳方法是什么?
编辑:我看到在某些例子中,实现是在模型中(它与视图模型不同),但在我的情况下,模型基本上是我创建时从数据库创建的POCO实体带有实体框架的edmx模型,所以我想避免修改这个实体所需要的,因为如果我不需要更新我的模型,我将不得不重新开始工作。感谢。
答案 0 :(得分:2)
如果您拥有保存数据的实体或自定义类型(如Person,Student等),则必须在Entity或Customtype中实现IDataErrorInfo。假设您有一个允许输入学生数据的View,并且您拥有ViewModel StudentViewModel,并且此ViewModel具有Student类型的属性Student,其属性(如Name,Age等)绑定到View of Controls。要激活验证和更改以反映在UI上,您必须在学生类中不在ViewModel 中实现IDataErrorInfo,并且您还必须在此类中实现 INotifyPropertyChanged 即可。所以我的理解是,如果你的ViewModel中有很少的属性,类型(字符串和值类型)并绑定到View,并且你想对它们应用Validations,那么你必须在ViewModel中实现IDataErrorInfo。如果您有CustomType / Entity,那么您必须在不在ViewModel中的那些类中实现接口。
我的理解不是必须在类中实现IDataErrorInfo和INotifyPropertyChanged,如果你想要激活验证,EndProperties(如Name,Age of Student)绑定到View的控件。
//Interface which has fields of Student class on which ValidationAttribute are to be applied
public interface IStudent
{
[Required(ErrorMessage = "Name is required")]
string Name { get; set; }
[Range(10, 90, ErrorMessage = "Age should be between 10 and 90")]
int Age { get; set; }
}
//Partial Class to implement IStudent
public partial class Student : IStudent
{
}
//POCO
public partial class Student : INotifyPropertyChanged
{
private string name;
private int age;
public string Name
{
get
{
return name;
}
set
{
name = value;
Notify("Name");
}
}
public int Age
{
get
{
return age;
}
set
{
age = value;
Notify("Age");
}
}
private void Notify(string propName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propName));
}
public event PropertyChangedEventHandler PropertyChanged;
}
答案 1 :(得分:2)
将验证逻辑与UI分开始终是个好主意。这样,使用IDataErrorInfo是正确的。
在视图模型和模型之间,我更喜欢在视图模型上实现IDataErrorInfo,因为UI使用此接口。您可以通过直接在测试代码中调用索引器来模拟UI,但如果您真的需要在业务逻辑层中使用验证逻辑,那么这样的调用没有多大意义。
在我们的项目中,验证是一个更独立的组件,可以通过配置由表示层和业务逻辑层使用。从视图模型的角度来看,它非常薄,只包含一个调用并在索引器中构建验证结果。
另外,另一个考虑因素是由.Net 4.5和Silverlight提供的INotifyDataErrorInfo。它提供了来自一个属性的更多验证结果和用于耗时验证的异步验证,这是我们计划更新到.Net 4.5之后的预期。
希望它可以帮到你。
答案 2 :(得分:2)
我同意绝大多数有关此主题的评论,但我正在回答为此界面提供“升级”。
我在IDataErrorInfo
界面看到的问题是它一次只能解决一个错误。因此,我在BaseDataType
类中添加了一个额外的字段(所有数据类型的基类):
protected ObservableCollection<string> errors = new ObservableCollection<string>();
然后我添加了以下属性:
// this just enables me to add into the error collection from outside this class
public ObservableCollection<string> ExternalErrors
{
get { return externalErrors; }
}
public override ObservableCollection<string> Errors
{
get
{
errors = new ObservableCollection<string>();
// add properties to validate
errors.AddUniqueIfNotEmpty(this["Property1ToValidate"]);
errors.AddUniqueIfNotEmpty(this["Property2ToValidate"]);
errors.AddUniqueIfNotEmpty(this["Property3ToValidate"]);
// add external errors (from view models)
errors.AddRange(ExternalErrors);
return errors;
}
}
public virtual bool HasError
{
get { return Errors != null && Errors.Count > 0; }
}
AddUniqueIfNotEmpty
方法是一种扩展方法,我相信你们都可以猜到它的作用。
使用它,我可以直接绑定到视图中的错误集合,甚至更好,使用HasError
绑定到BoolToVisibilityConverter
属性,以隐藏显示错误的控件收集是空的。
答案 3 :(得分:1)
<TextBox Text="{Binding Path=MyCoolProperty, ValidationOnDataErrors=true}"
也许我会错过一些东西,但是如果你有这样的绑定 - 你的“MyCoolProperty”类必须实现INotifyPropertyChanges和IDataErrorInfo - 否则它将不起作用。
所以我想问的问题不是:“应该实现IDataErrorInfo”但是也许如何实现IDataErrorInfo