2011-02-04 5 views
14

Я прочитал много сообщений в блоге по WPF Validation и по DataAnnotations. Мне было интересно, есть ли чистый способ использовать DataAnnotations как ValidationRules для моего лица.WPF-привязка: используйте 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 повидаться свойство Возраст и искать DataAnnotation немного похоже это:

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

Любые идеи, если это возможно?

+0

Вот [Блогпост] (http://translate.google.com/translate?sl=fr&tl=en&js=n&prev=_t&hl=en&ie=UTF-8&layout=2&eotf=1&u=http%3A%2F% 2Fwww.nmediasolutions.com% 2Fblogue% 2Fdeveloppement-dapplications% 2Fvalidation-dpendante-du-contexte% 2F & act = url) об этом виде проверки, но зависит от контекста (Перевод с французского на английский) –

+0

Вот еще [blogpost] (http://translate.google.com/translate?hl=en&sl=fr&tl=en&u=http%3A%2F%2Fwww.nmediasolutions.com%2Fblogue%2Fdeveloppement-dapplications%2Fvalidation-entite-message-erreur-infobulle-tooltip- wpf% 2F) о показе всплывающей подсказки для этой аннотации (Перевод с французского на английский) –

+0

Посмотрите на видео [MVVM предприятия в WPF: проверка ViewModel с использованием аннотаций данных] (http://www.youtube.com/watch ? v = lool8U t58Xw). Я думаю, что это хорошее решение для вашей проблемы. –

ответ

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

Source и Source

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

+2

Вы должны знать, что ваша реализация IDataErrorInfo не будет вызвана до назначения значения. Поэтому, если другой объект подписан на PropertyChanged вашего DTO, тогда они будут использовать значение, которое в скором времени может быть отмечено вашим кодом недействительным. –

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

EDIT:

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

UPDATE:

Historical representation вышеупомянутой ссылке.

+0

Он работает нормально, но использует DataAnnotations. Это была бы красота. –

+0

@Philippe Вы можете сделать это в SL http://msdn.microsoft.com/en-us/library/dd901590(VS.95).aspx, однако не в WPF из коробки; это может быть сделано, хотя ... обновленный ответ ... –

+0

Итак, вы говорите, что механизм проверки DataAnnotation существует только в SilverLight, а не в WPF? Я пробую пример блога. Он работает, но вы должны вручную проверить все DataAnnotation в каждом свойстве. Я искал, чтобы WPF-структура была как-то сглажена, чтобы сделать, как SilverLight, и автоматически проверять DataAnnotations на мои свойства. –

0

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

Но с WinForms я использовал Validation Application Block from the Entlib и реализованы IDataErrorInfo (на самом деле IDXDataErrorInfo, потому что мы работаем с элементами управления DevExpress) на базовый объект (бизнес-объект), и это работает очень хорошо!

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

0

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

0

Недавно у меня была та же идея с использованием API аннотации данных для проверки классов EF Code First POCO в WPF. Как и сообщение 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; } 
}