Связывание WPF: используйте DataAnnotations для ValidationRules
Я прочитал много сообщений в блоге о WPF Validation и 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);
}
}
Любые идеи, если это возможно?
Ответы
Ответ 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;
Источник
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"
, и это прекрасный способ обхода Aaron с DataAnnotation.
Ответ 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}" />
ИЗМЕНИТЬ:
Если вы хотите использовать аннотации данных, вы можете следить за этим сообщением в котором описывается, как выполнить задачу.
UPDATE
Историческое представление вышеупомянутой ссылки.
Ответ 3
Звучит неплохо Аарон. Я просто в WPF и буду изучать привязки данных на следующей неделе на работе;) Поэтому не могу полностью судить о вашем ответе...
Но с winforms я использовал блок приложения проверки работоспособности из Entlib и реализовал IDataErrorInfo (фактически IDXDataErrorInfo, потому что мы работаем с элементами управления DevExpress) на базовом объекте (бизнес-объект), и это работает очень хорошо!
Это немного сложнее, чем решение, которое вы набросали таким образом, чтобы разместить логику проверки на объекте, а не в реализации интерфейса. Сделать его более ООП и ремонтопригодным. В ID (XD) ataErrorInfo я просто вызываю Validation.Validate(this) или даже лучше получаю валидатор для свойства, которому вызван интерфейс, и проверки конкретного валидатора. Не забудьте также вызвать [SelfValidation] из-за проверки для комбинаций свойств;)
Ответ 4
Вам может быть интересно использовать приложение BookLibrary WPF Application Framework (WAF). Он использует атрибуты проверки DataAnnotations вместе с привязкой WPF.
Ответ 5
Недавно у меня была такая же идея с использованием 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; }
}