WPF Binding : Use DataAnnotations for ValidationRules

前端 未结 5 557
眼角桃花
眼角桃花 2020-12-09 04:59

I have read a lot of Blog post on WPF Validation and on DataAnnotations. I was wondering if there is a clean way to use DataAnnotations as Va

相关标签:
5条回答
  • 2020-12-09 05:37

    The closest approach I found is :

    // This loop into all DataAnnotations and return all errors strings
    protected string ValidateProperty(object value, string propertyName)
    {
      var info = this.GetType().GetProperty(propertyName);
      IEnumerable<string> errorInfos =
            (from va in info.GetCustomAttributes(true).OfType<ValidationAttribute>()
             where !va.IsValid(value)
             select va.FormatErrorMessage(string.Empty)).ToList();
    
    
      if (errorInfos.Count() > 0)
      {
        return errorInfos.FirstOrDefault<string>();
      }
      return null;
    

    Source

    public class PersonEntity : IDataErrorInfo
    {
    
        [StringLength(50, MinimumLength = 1, ErrorMessage = "Error Msg.")]
        public string Name
        {
          get { return _name; }
          set
          {
            _name = value;
            PropertyChanged("Name");
          }
        }
    
    public string this[string propertyName]
        {
          get
          {
            if (porpertyName == "Name")
            return ValidateProperty(this.Name, propertyName);
          }
        }
    }
    

    Source and Source

    That way, the DataAnnotation works fine, I got a minimum to do on the XAML ValidatesOnDataErrors="True" and it's a fine workaround of Aaron post with the DataAnnotation.

    0 讨论(0)
  • 2020-12-09 05:47

    Recently I've had the same idea using the Data Annotation API to validate EF Code First POCO classes in WPF. Like Philippe's post my solution uses reflection, but all necessary code is included in a generic validator.

    internal class ClientValidationRule : GenericValidationRule<Client> { }
    
    internal class GenericValidationRule<T> : ValidationRule
    {
      public override ValidationResult Validate(object value, CultureInfo cultureInfo)
      {
        string result = "";
        BindingGroup bindingGroup = (BindingGroup)value;
        foreach (var item in bindingGroup.Items.OfType<T>()) {
          Type type = typeof(T);
          foreach (var pi in type.GetProperties()) {
            foreach (var attrib in pi.GetCustomAttributes(false)) {
              if (attrib is System.ComponentModel.DataAnnotations.ValidationAttribute) {
                var validationAttribute = attrib as System.ComponentModel.DataAnnotations.ValidationAttribute;
                var val = bindingGroup.GetValue(item, pi.Name);
                if (!validationAttribute.IsValid(val)) { 
                  if (result != "")
                    result += Environment.NewLine;
                  if (string.IsNullOrEmpty(validationAttribute.ErrorMessage))
                    result += string.Format("Validation on {0} failed!", pi.Name);
                  else
                    result += validationAttribute.ErrorMessage;
                }
              }
            }
          }
        }
        if (result != "")
          return new ValidationResult(false, result);
        else 
          return ValidationResult.ValidResult;
      }
    }
    

    The code above shows a ClientValidatorRule which is derived from the generic GenericValidationRule class. The Client class is my POCO class which will be validated.

    public class Client {
        public Client() {
          this.ID = Guid.NewGuid();
        }
    
        [Key, ScaffoldColumn(false)]
        public Guid ID { get; set; }
    
        [Display(Name = "Name")]
        [Required(ErrorMessage = "You have to provide a name.")]
        public string Name { get; set; }
    }
    
    0 讨论(0)
  • 2020-12-09 05:48

    Sounds good Aaron. I'm just into WPF and will study databindings next week at work ;) So cannot completely judge your answer...

    But with winforms I have used Validation Application Block from the Entlib and implemented IDataErrorInfo (actually IDXDataErrorInfo because we work with DevExpress controls) on a base entity (business object) and that works pretty fine!

    It's a bit more sophisticated than the solution you sketched in this way that you place your validation logic on the object and not in the interface implementation. Making it more OOP and maintainable. At the ID(XD)ataErrorInfo I just call Validation.Validate(this), or even better get the validator for the property that the interface is called for and validate the specific validator. Don't forget to call the [SelfValidation] as well because of validation for combinations of properties ;)

    0 讨论(0)
  • 2020-12-09 05:54

    In your model you could implement IDataErrorInfo and do something like this...

    string IDataErrorInfo.this[string columnName]
    {
        get
        {
            if (columnName == "Age")
            {
                if (Age < 0 ||
                    Age > 120)
                {
                    return "You must be between 1 - 120";
                }
            }
            return null;
        }
    }
    

    You will also need to notify the binding target of the newly defined behavior.

    <TextBox Text="{Binding Age, ValidatesOnDataErrors=True}" />
    

    EDIT:

    If you only want to use Data Annotations you can follow this blog post which outlines how to accomplish the task.

    UPDATE:

    Historical representation of the aforementioned link.

    0 讨论(0)
  • 2020-12-09 05:54

    You might be interested in the BookLibrary sample application of the WPF Application Framework (WAF). It uses the DataAnnotations Validation attributes together with WPF Binding.

    0 讨论(0)
提交回复
热议问题