65.9K
CodeProject 正在变化。 阅读更多。
Home

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

starIconstarIconstarIconstarIconstarIcon

5.00/5 (5投票s)

2016年12月16日

CPOL
viewsIcon

12202

downloadIcon

205

扩展 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

原文 

© . All rights reserved.