WPF数据绑定:使用DataAnnotations进行验证规则

13

我已经阅读了很多关于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);
  }
}

您有没有想过这是否可能实现?


这是一篇关于这种依赖上下文的验证的博客文章,翻译自法语。 - Philippe Lavoie
这是另一篇关于如何为此注释显示工具提示的博客文章(从法语翻译成英语)。 - Philippe Lavoie
请查看视频Enterprise MVVM in WPF: ViewModel Validation using Data Annotations。我认为这是您问题的一个不错解决方案。 - Jonas Benz
5个回答

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

源代码源代码

这样,数据注释工作正常,我只需要在XAML上 ValidatesOnDataErrors="True" 最小化一下操作,这是 Aaron 帖子中使用数据注释的好解决办法。


2
你应该知道,在给值赋值之前,你的IDataErrorInfo实现不会被调用。因此,如果另一个对象已经订阅了你DTO的PropertyChanged事件,那么它们将使用很快可能被你的代码标记为无效的值。 - Lonli-Lokli

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}" />

编辑

如果您只想使用数据注释,您可以按照此博客文章中的说明完成任务。

更新

历史版本上述链接的内容。


它运行良好,但它确实使用了DataAnnotations。那将是一种美妙的事情。 - Philippe Lavoie
@Philippe,你可以在SL中完成这个任务http://msdn.microsoft.com/en-us/library/dd901590(VS.95).aspx,但是在WPF中不能直接完成;不过还是有办法的...已更新答案... - Aaron McIver
所以你的意思是,检查DataAnnotation的机制只存在于SilverLight中而不是WPF中?我尝试了博客文章中的示例。它可以工作,但您必须手动检查每个属性中的所有DataAnnotation。我正在寻找是否有类似SilverLight的WPF框架,并且可以自动检查我的属性上的DataAnnotations。 - Philippe Lavoie
1
@Philippe 是的,内置行为是SL;而不是WPF。 - Aaron McIver
教程链接已失效。 - Ben
@Ben 使用网络档案更新了链接。 - Aaron McIver

0

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

0

听起来不错,艾伦。我正在学习 WPF 并会在下周工作中研究数据绑定;) 所以不能完全评判你的答案...

但是对于 Winforms,我使用了 Entlib 的验证应用程序块,并在基础实体(业务对象)上实现了 IDataErrorInfo(实际上是 IDXDataErrorInfo,因为我们使用 DevExpress 控件),这样运行得非常好!

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


0

你可能会对WPF应用程序框架(WAF)BookLibrary示例应用程序感兴趣。它使用DataAnnotations验证属性与WPF绑定一起使用。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接