WPF绑定:对ValidationRules使用DataAnnotations

时间:2011-02-04 21:17:25

标签: wpf binding data-annotations validationrules

我在WPF验证和DataAnnotations上阅读了很多博客文章。我想知道是否有一种干净的方式将DataAnnotations用作ValidationRules用于我的实体。

所以没有这个(Source):

<Binding Path="Age" Source="{StaticResource ods}" ... >
  <Binding.ValidationRules>
    <c:AgeRangeRule Min="21" Max="130"/>
  </Binding.ValidationRules>
</Binding>

你必须拥有

的地方
public class AgeRangeRule : ValidationRule 
{...}

我希望WPF Binding能够看到Age属性并查找DataAnnotation,如下所示:

[Range(1, 120)]
public int Age
{
  get { return _age; }
  set
  {
    _age = value;
    RaisePropertyChanged<...>(x => x.Age);
  }
}

如果有可能,有什么想法吗?

5 个答案:

答案 0 :(得分:8)

我找到的最接近的方法是:

// 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);
      }
    }
}

SourceSource

这样,DataAnnotation工作得很好,我在XAML ValidatesOnDataErrors="True"上做了最少的事情,这是使用DataAnnotation的Aaron帖子的一个很好的解决方法。

答案 1 :(得分:5)

在你的模型中,你可以实现IDataErrorInfo并执行类似的操作......

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

您还需要通知绑定目标新定义的行为。

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

编辑

如果您只想使用数据注释,可以按照blog post进行操作,其中概述了如何完成任务。

<强>更新

Historical representation上述链接。

答案 2 :(得分:0)

听起来不错的亚伦。我刚刚进入WPF并将在下周的工作中学习数据绑定;)所以不能完全判断你的答案......

但是使用winforms我已经使用Validation Application Block from the Entlib并在基础实体(业务对象)上实现了IDataErrorInfo(实际上是IDXDataErrorInfo,因为我们使用DevExpress控件)并且工作得非常好!

它比您以这种方式描绘的解决方案更复杂,您将验证逻辑放在对象上,而不是放在接口实现中。使其更具OOP和可维护性。在ID(XD)ataErrorInfo中我只调用Validation.Validate(this),或者甚至更好地获取调用该接口的属性的验证器并验证特定的验证器。由于对属性组合的验证,不要忘记调用[SelfValidation];)

答案 3 :(得分:0)

您可能对 WPF Application Framework (WAF) BookLibrary 示例应用感兴趣。它使用DataAnnotations Validation属性和WPF Binding。

答案 4 :(得分:0)

最近,我有同样的想法使用Data Annotation API来验证WPF中的EF Code First POCO类。与Philippe的帖子一样,我的解决方案使用反射,但所有必要的代码都包含在通用验证器中。

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;
  }
}

上面的代码显示了一个ClientValidatorRule,它源自通用的GenericValidationRule类。 Client类是我的POCO类,将被验证。

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; }
}