Связывание WPF: используйте DataAnnotations для ValidationRules - PullRequest
13 голосов
/ 05 февраля 2011

Я прочитал много постов в блоге о проверке WPF и о DataAnnotations. Мне было интересно, есть ли чистый способ использовать DataAnnotations как ValidationRules для моей сущности.

Таким образом, вместо этого ( Источник ):

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

Есть идеи, если это возможно?

Ответы [ 5 ]

8 голосов
/ 07 февраля 2011

Ближайший подход, который я нашел:

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

Источник

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

Источник и Источник

Таким образом, DataAnnotation работает отлично, у меня есть минимум для XAML ValidatesOnDataErrors="True", и это хороший обходной путь для сообщения Аарона с DataAnnotation.

5 голосов
/ 05 февраля 2011

В вашей модели вы можете реализовать 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}" />

РЕДАКТИРОВАТЬ :

Если вы хотите использовать только аннотации данных, вы можете следовать этому сообщению в блоге , в котором описывается, как выполнить задачу.

ОБНОВЛЕНИЕ :

Историческое представление вышеупомянутой ссылки.

0 голосов
/ 13 декабря 2013

Недавно у меня возникла та же идея использовать API аннотации данных для проверки классов POCO EF Code First в WPF. Как и сообщение Филиппа, мое решение использует рефлексию, но весь необходимый код включен в общий валидатор.

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 голосов
/ 05 февраля 2011

Вас может заинтересовать BookLibrary пример приложения WPF Application Framework (WAF) .Он использует атрибуты проверки DataAnnotations вместе с привязкой WPF.

0 голосов
/ 05 февраля 2011

Звучит хорошо, Аарон.Я только в WPF и буду изучать привязки данных на следующей неделе на работе;) Так что не могу полностью судить о вашем ответе ...

Но с winforms я использовал Блок приложения проверки из Entlib иреализовал IDataErrorInfo (на самом деле IDXDataErrorInfo , потому что мы работаем с элементами управления DevExpress) на базовом объекте (бизнес-объекте), и это работает довольно хорошо!

Это немного сложнее, чем решение, которое вы набросали таким образом, что вы помещаете логику проверки в объект, а не в реализацию интерфейса.Делая это более ООП и ремонтопригодным.В ID (XD) ataErrorInfo я просто вызываю Validation.Validate (this) или, что еще лучше, получаю валидатор для свойства, для которого вызывается интерфейс, и проверяю конкретный валидатор.Не забудьте также вызвать [SelfValidation] из-за проверки комбинаций свойств;)

...