WPF提供了非常好的数据验证模式。
具体不理解的请参考《Validation in Windows Presentation Foundation》
如果让你设计在V-VM-M 中数据的验证 ,你更喜欢把它放到那一层呢?当然如果我们可以说那一层都无所谓,比较数据都是通过View输入的。最终的反馈也是在View层显示,那View层是最直接最方便的。 那WPF设计者的确为之中方式提供了很方便的编写方式。只要在Binding中使用ValidationRules指定具体的验证规则就OK了,而且一般来说我们要验证的数据不会太多,无非是不能为空,或者满足某个正则的约定。所以这个已经非常方便了。
由于某些原因有很多时候我们的Model层可能本身就有了ValidationAttribute特性。那我们应该去应用这个特性。当然还有一个理由就是我们的数据对象被多出创建,或者说我们不想在XAML中每次都填写验证方式。
那么我们可以这样去写。
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
public abstract class ValidationViewModelBase : ViewModelBase, IDataErrorInfo
{
private readonly TypesContent _typesContent;
private readonly Type _thisType;
protected ValidationViewModelBase()
{
this._thisType = GetType();
this._typesContent = new TypesContent(this._thisType);
}
#region Implementation of IDataErrorInfo
public string this[string columnName]
{
get
{
PropertyInfo info = this._typesContent.GetPropertyInfo(
this._thisType, columnName);
var validationAttributes =
this._typesContent.GetValidationAttribute(this._thisType, columnName);
var value = info.GetValue(this, null);
var errors = new List<string>();
foreach (var validationAttribute in validationAttributes)
{
if (!validationAttribute.IsValid(value))
{
errors.Add(validationAttribute.ErrorMessage);
}
}
OnPropertyChanged("Error");
return string.Join(Environment.NewLine, errors.ToArray());
}
}
public string Error
{
get
{
var propertyInfos = this._typesContent.GetPropertInfos(this._thisType);
var errorlist = new List<string>();
foreach (var info in propertyInfos)
{
var validationAttributes =
this._typesContent.GetValidationAttribute(
this._thisType, info.Name);
var value = info.GetValue(this, null);
var errors = validationAttributes.Where(v => !v.IsValid(value))
.Select(v => v.ErrorMessage).ToArray();
errorlist.AddRange(errors);
}
return string.Join(Environment.NewLine, errorlist.ToArray());
}
}
#endregion
private class TypePropertyContent
{
private readonly Type _type;
private readonly Dictionary<string, PropertyInfo> _propertyInfos =
new Dictionary<string, PropertyInfo>();
private readonly Dictionary<string, ValidationAttribute[]> _validators =
new Dictionary<string, ValidationAttribute[]>();
public TypePropertyContent(Type type)
{
this._type = type;
this.LoadData();
}
private void LoadData()
{
var classType = this._type;
var properties = classType.GetProperties();
foreach (var propertyInfo in properties)
{
var customAttributes =
propertyInfo.GetCustomAttributes(
typeof(ValidationAttribute), true);
if (customAttributes.Length > 0)
{
this._validators.Add(
propertyInfo.Name, (ValidationAttribute[]) customAttributes);
this._propertyInfos.Add(propertyInfo.Name, propertyInfo);
}
}
}
public PropertyInfo GetPropertInfo(string propertyName)
{
return this._propertyInfos.ContainsKey(propertyName)
? this._propertyInfos[propertyName]
: null;
}
public PropertyInfo[] GetPropertInfos()
{
return this._propertyInfos.Values.ToArray();
}
public ValidationAttribute[] GetValidationAttribute(string propertyName)
{
return this._validators.ContainsKey(propertyName)
? this._validators[propertyName]
: null;
}
}
private class TypesContent
{
private static readonly Dictionary<Type, TypePropertyContent> _types =
new Dictionary<Type, TypePropertyContent>();
public TypesContent(Type type)
{
if (!_types.ContainsKey(type))
{
_types.Add(type, new TypePropertyContent(type));
}
}
public PropertyInfo GetPropertyInfo(Type type, string propertyName)
{
if (_types.ContainsKey(type))
{
var propertyContent = _types[type];
return propertyContent.GetPropertInfo(propertyName);
}
return null;
}
public ValidationAttribute[] GetValidationAttribute(
Type type, string propertyName)
{
if (_types.ContainsKey(type))
{
var typePropertyContent = _types[type];
return typePropertyContent.GetValidationAttribute(propertyName);
}
return null;
}
public PropertyInfo[] GetPropertInfos(Type type)
{
return _types.ContainsKey(type) ? _types[type].GetPropertInfos() : null;
}
}
}
public class ViewModelBase : INotifyPropertyChanged
{
#region INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged = delegate { };
protected virtual void OnPropertyChanged(PropertyChangedEventArgs eventArgs)
{
this.PropertyChanged(this, eventArgs);
}
protected virtual void OnPropertyChanged(string propertyName)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
}
//ViewModel
public class PersonViewModel : ValidationViewModelBase
{
private string _name;
[RegularExpression(@"^\w+$", ErrorMessage = "format error")]
public string Name
{
get
{
return this._name;
}
set
{
this._name = value;
OnPropertyChanged("Name");
}
}
}
//View like this:
<TextBox>
<TextBox.Text>
<Binding Path=".Name"
Mode="TwoWay"
UpdateSourceTrigger="PropertyChanged"
ValidatesOnDataErrors="True"
ValidatesOnExceptions="True"
NotifyOnValidationError="True">
</Binding>
</TextBox.Text>
</TextBox>
{
private readonly TypesContent _typesContent;
private readonly Type _thisType;
protected ValidationViewModelBase()
{
this._thisType = GetType();
this._typesContent = new TypesContent(this._thisType);
}
#region Implementation of IDataErrorInfo
public string this[string columnName]
{
get
{
PropertyInfo info = this._typesContent.GetPropertyInfo(
this._thisType, columnName);
var validationAttributes =
this._typesContent.GetValidationAttribute(this._thisType, columnName);
var value = info.GetValue(this, null);
var errors = new List<string>();
foreach (var validationAttribute in validationAttributes)
{
if (!validationAttribute.IsValid(value))
{
errors.Add(validationAttribute.ErrorMessage);
}
}
OnPropertyChanged("Error");
return string.Join(Environment.NewLine, errors.ToArray());
}
}
public string Error
{
get
{
var propertyInfos = this._typesContent.GetPropertInfos(this._thisType);
var errorlist = new List<string>();
foreach (var info in propertyInfos)
{
var validationAttributes =
this._typesContent.GetValidationAttribute(
this._thisType, info.Name);
var value = info.GetValue(this, null);
var errors = validationAttributes.Where(v => !v.IsValid(value))
.Select(v => v.ErrorMessage).ToArray();
errorlist.AddRange(errors);
}
return string.Join(Environment.NewLine, errorlist.ToArray());
}
}
#endregion
private class TypePropertyContent
{
private readonly Type _type;
private readonly Dictionary<string, PropertyInfo> _propertyInfos =
new Dictionary<string, PropertyInfo>();
private readonly Dictionary<string, ValidationAttribute[]> _validators =
new Dictionary<string, ValidationAttribute[]>();
public TypePropertyContent(Type type)
{
this._type = type;
this.LoadData();
}
private void LoadData()
{
var classType = this._type;
var properties = classType.GetProperties();
foreach (var propertyInfo in properties)
{
var customAttributes =
propertyInfo.GetCustomAttributes(
typeof(ValidationAttribute), true);
if (customAttributes.Length > 0)
{
this._validators.Add(
propertyInfo.Name, (ValidationAttribute[]) customAttributes);
this._propertyInfos.Add(propertyInfo.Name, propertyInfo);
}
}
}
public PropertyInfo GetPropertInfo(string propertyName)
{
return this._propertyInfos.ContainsKey(propertyName)
? this._propertyInfos[propertyName]
: null;
}
public PropertyInfo[] GetPropertInfos()
{
return this._propertyInfos.Values.ToArray();
}
public ValidationAttribute[] GetValidationAttribute(string propertyName)
{
return this._validators.ContainsKey(propertyName)
? this._validators[propertyName]
: null;
}
}
private class TypesContent
{
private static readonly Dictionary<Type, TypePropertyContent> _types =
new Dictionary<Type, TypePropertyContent>();
public TypesContent(Type type)
{
if (!_types.ContainsKey(type))
{
_types.Add(type, new TypePropertyContent(type));
}
}
public PropertyInfo GetPropertyInfo(Type type, string propertyName)
{
if (_types.ContainsKey(type))
{
var propertyContent = _types[type];
return propertyContent.GetPropertInfo(propertyName);
}
return null;
}
public ValidationAttribute[] GetValidationAttribute(
Type type, string propertyName)
{
if (_types.ContainsKey(type))
{
var typePropertyContent = _types[type];
return typePropertyContent.GetValidationAttribute(propertyName);
}
return null;
}
public PropertyInfo[] GetPropertInfos(Type type)
{
return _types.ContainsKey(type) ? _types[type].GetPropertInfos() : null;
}
}
}
public class ViewModelBase : INotifyPropertyChanged
{
#region INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged = delegate { };
protected virtual void OnPropertyChanged(PropertyChangedEventArgs eventArgs)
{
this.PropertyChanged(this, eventArgs);
}
protected virtual void OnPropertyChanged(string propertyName)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
}
//ViewModel
public class PersonViewModel : ValidationViewModelBase
{
private string _name;
[RegularExpression(@"^\w+$", ErrorMessage = "format error")]
public string Name
{
get
{
return this._name;
}
set
{
this._name = value;
OnPropertyChanged("Name");
}
}
}
//View like this:
<TextBox>
<TextBox.Text>
<Binding Path=".Name"
Mode="TwoWay"
UpdateSourceTrigger="PropertyChanged"
ValidatesOnDataErrors="True"
ValidatesOnExceptions="True"
NotifyOnValidationError="True">
</Binding>
</TextBox.Text>
</TextBox>