【问题标题】:WPF Binding : Use DataAnnotations for ValidationRulesWPF 绑定:对 ValidationRules 使用 DataAnnotations
【发布时间】:2011-06-21 15:25:48
【问题描述】:

我已经阅读了很多关于 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 绑定去查看 Age 属性并查找 DataAnnotation 有点像这样:

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

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

【问题讨论】:

标签: wpf binding data-annotations validationrules


【解决方案1】:

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

// 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" 上要做的最少,这是 Aaron 使用 DataAnnotation 发布帖子的一个很好的解决方法。

【讨论】:

  • 您应该知道,在分配值之前不会调用您的 IDataErrorInfo 实现。因此,如果另一个对象订阅了您的 DTO 的 PropertyChanged,那么它们将使用很快可能被您的代码标记为无效的值。
【解决方案2】:

在您的模型中,您可以实现 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

【讨论】:

  • 它工作正常,但它确实使用了 DataAnnotations。那将是一个美丽。
  • @Philippe 您可以在 SL msdn.microsoft.com/en-us/library/dd901590(VS.95).aspx 中执行此操作,但不能在 WPF 中进行开箱即用;虽然可以做到...更新答案...
  • 所以您是说检查 DataAnnotation 的机制仅存在于 SilverLight 中而不存在于 WPF 中?我尝试了博客文章示例。它可以工作,但您必须手动检查每个属性中的所有 DataAnnotation。我正在搜索 WPF 框架是否能够像 SilverLight 一样自动检查我的属性上的 DataAnnotations。
  • @Philippe 是的;内置行为是 SL;不是 WPF
  • 教程链接失效
【解决方案3】:

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

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

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

【讨论】:

    【解决方案4】:

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

    【讨论】:

      【解决方案5】:

      最近我有同样的想法,使用数据注释 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;
        }
      }
      

      上面的代码显示了一个派生自通用 GenericValidationRule 类的 ClientValidatorRule。 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; }
      }
      

      【讨论】:

        猜你喜欢
        • 2014-07-05
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-10-30
        • 1970-01-01
        • 1970-01-01
        • 2017-05-06
        相关资源
        最近更新 更多