使用属性在 WPF 中进行条件验证





5.00/5 (5投票s)
扩展 Data Annotations 库以支持 WPF 中的条件验证
引言
在下面的图片中,即使复选框(“有经验”条件)为假且文本框被禁用,验证也会被触发。
在下面的动图中,只有当复选框(“有经验”条件)为真时,验证才会触发。
我们将看到如何使用特性在 WPF 中实现条件验证。
背景
在WPF 中的验证的各种方法中,我实现了 IDataErrorInfo 以促进验证。
使用代码
在这个例子中,我们将看到如何扩展 Range 特性以支持条件验证并调整 IDataErrorInfo 实现。
RangeAttribute 的扩展。
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
[Conditional]
public class RangeIfAttribute : RangeAttribute
{
private string _dependentProperty;
public RangeIfAttribute(double minimum, double maximum, string dependentProperty)
: base(minimum, maximum)
{
this._dependentProperty = dependentProperty;
}
public RangeIfAttribute(int minimum, int maximum, string dependentProperty)
: base(minimum, maximum)
{
this._dependentProperty = dependentProperty;
}
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
var containerType = validationContext.ObjectInstance.GetType();
var field = containerType.GetProperty(this._dependentProperty);
bool dependentvalue = (bool)field.GetValue(validationContext.ObjectInstance);
if (dependentvalue)
{
return base.IsValid(value, validationContext);
}
return ValidationResult.Success;
}
}
用 RangeIfAttribute 装饰的 ViewModel 属性
public class CandidateViewModel : ViewModelBase
{
private int _yearsOfExperience;
private bool _isExperienced;
[RangeIf(1, 100, "IsExperienced", ErrorMessage = "Years Of Experience must be greater than 0")]
public int YearsOfExperience
{
get
{
return this._yearsOfExperience;
}
set
{
this._yearsOfExperience = value;
}
}
public bool IsExperienced
{
get
{
return this._isExperienced;
}
set
{
this._isExperienced = value;
this.OnPropertyChanged("YearsOfExperience"); /*Property Changed callback should be fired for
the dependent property 'YearsOfExperience'*/
}
}
}
实现 IDataErrorInfo 的 ViewModelBase 类。如果验证属性是**条件**属性,则我调用 GetValidationResult,并将 ViewModel 实例作为验证上下文参数传递。
public class ViewModelBase : IDataErrorInfo, INotifyPropertyChanged
{
private readonly Dictionary<string, Func<ViewModelBase, object>> propertyGetters;
private readonly Dictionary<string, ValidationAttribute[]> validators;
public ViewModelBase()
{
this.validators = this.GetType()
.GetProperties()
.Where(p => this.GetValidations(p).Length != 0)
.ToDictionary(p => p.Name, p => this.GetValidations(p));
this.propertyGetters = this.GetType()
.GetProperties()
.Where(p => this.GetValidations(p).Length != 0)
.ToDictionary(p => p.Name, p => this.GetValueGetter(p));
}
public string this[string propertyName]
{
get
{
if (this.propertyGetters.ContainsKey(propertyName))
{
var errorMessages = this.validators[propertyName]
.Where(attribute => !this.Validate(attribute, propertyName))
.Select(attribute => attribute.ErrorMessage).ToList();
return string.Join(Environment.NewLine, errorMessages);
}
return string.Empty;
}
}
private bool Validate(ValidationAttribute validationAttribute, string propertyName)
{
var propertyValue = this.propertyGetters[propertyName](this);
if (IsConditionalValidationAttribute(validationAttribute))
{
return validationAttribute.GetValidationResult(propertyValue, new ValidationContext(this)) == ValidationResult.Success;
}
return validationAttribute.IsValid(propertyValue);
}
private bool IsConditionalValidationAttribute(ValidationAttribute validationAttribute)
{
return validationAttribute.GetType().GetCustomAttributes().Any(x => x.GetType() == typeof(ConditionalAttribute));
}
}
在 Wpf 中处理条件验证有几种方法,例如使用数据触发器来禁用错误模板,但我认为这是一种简洁的解决方案。 欢迎提出任何意见!
参考文献
https://forums.asp.net/t/1924941.aspx?Conditional+Validation+using+DataAnnotation
历史
16-12-2016
原文