ObservableValidator
Это ObservableValidator
базовый класс, реализующий INotifyDataErrorInfo
интерфейс, предоставляющий поддержку проверки свойств, предоставляемых другим модулям приложений. Он также наследует от ObservableObject
, поэтому он реализует INotifyPropertyChanged
и INotifyPropertyChanging
также. Его можно использовать в качестве отправной точки для всех типов объектов, которые должны поддерживать уведомления об изменении свойств и проверку свойств.
API платформы: ObservableValidator, ObservableObject
Принцип работы
ObservableValidator
имеет следующие основные функции:
- Она предоставляет базовую реализацию для
INotifyDataErrorInfo
предоставленияErrorsChanged
события и других необходимых API. - Он предоставляет ряд дополнительных
SetProperty
перегрузок (наряду с теми, которые предоставляются базовымObservableObject
классом), которые предлагают возможность автоматической проверки свойств и повышения необходимых событий перед обновлением их значений. - Он предоставляет ряд
TrySetProperty
перегрузок, которые похожиSetProperty
на то, но с возможностью обновления только целевого свойства, если проверка выполнена успешно, и возвратить созданные ошибки (если таковые имеются) для дальнейшего проверки. - Он предоставляет
ValidateProperty
метод, который может быть полезен для ручной активации проверки определенного свойства в случае, если его значение не было обновлено, но его проверка зависит от значения другого свойства, которое вместо этого было обновлено. - Он предоставляет
ValidateAllProperties
метод, который автоматически выполняет проверку всех свойств общедоступного экземпляра в текущем экземпляре, если они имеют по крайней мере один[ValidationAttribute]
применен к ним. - Он предоставляет
ClearAllErrors
метод, который может быть полезен при сбросе модели, привязанной к какой-то форме, которую пользователь может снова заполнить. - Он предлагает ряд конструкторов, которые позволяют передавать различные параметры для инициализации
ValidationContext
экземпляра, который будет использоваться для проверки свойств. Это может быть особенно полезно при использовании настраиваемых атрибутов проверки, которые могут требовать правильной работы дополнительных служб или параметров.
Простое свойство
Ниже приведен пример реализации свойства, поддерживающего как уведомления об изменениях, так и проверку:
public class RegistrationForm : ObservableValidator
{
private string name;
[Required]
[MinLength(2)]
[MaxLength(100)]
public string Name
{
get => name;
set => SetProperty(ref name, value, true);
}
}
Здесь мы вызываем метод, предоставляемый SetProperty<T>(ref T, T, bool, string)
ObservableValidator
, и этот дополнительный bool
набор true
параметров указывает, что мы также хотим проверить свойство при обновлении его значения. ObservableValidator
автоматически выполняет проверку по каждому новому значению, используя все проверки, указанные атрибутами, примененными к свойству. Другие компоненты (например, элементы управления пользовательским интерфейсом) затем могут взаимодействовать с моделем представления и изменять их состояние, чтобы отразить ошибки, которые в настоящее время присутствуют в моде представления, регистрируясь ErrorsChanged
и используя GetErrors(string)
метод для получения списка ошибок для каждого измененного свойства.
Пользовательские методы проверки
Иногда для проверки свойства требуется доступ к дополнительным службам, данным или другим API- интерфейсам представления. Существуют различные способы добавления пользовательской проверки в свойство в зависимости от сценария и требуемого уровня гибкости. Ниже приведен пример использования [CustomValidationAttribute]
типа для указания необходимости вызова определенного метода для выполнения дополнительной проверки свойства:
public class RegistrationForm : ObservableValidator
{
private readonly IFancyService service;
public RegistrationForm(IFancyService service)
{
this.service = service;
}
private string name;
[Required]
[MinLength(2)]
[MaxLength(100)]
[CustomValidation(typeof(RegistrationForm), nameof(ValidateName))]
public string Name
{
get => this.name;
set => SetProperty(ref this.name, value, true);
}
public static ValidationResult ValidateName(string name, ValidationContext context)
{
RegistrationForm instance = (RegistrationForm)context.ObjectInstance;
bool isValid = instance.service.Validate(name);
if (isValid)
{
return ValidationResult.Success;
}
return new("The name was not validated by the fancy service");
}
}
В этом случае у нас есть статический ValidateName
метод, который будет выполнять проверку свойства Name
через службу, внедренную в наш видмодель. Этот метод получает name
значение свойства и ValidationContext
используемый экземпляр, который содержит такие вещи, как экземпляр viewmodel, имя проверяемого свойства, а также поставщик услуг и некоторые пользовательские флаги, которые мы можем использовать или задать. В этом случае мы извлекаем RegistrationForm
экземпляр из контекста проверки, а затем используем внедренную службу для проверки свойства. Обратите внимание, что эта проверка будет выполняться рядом с теми, которые указаны в других атрибутах, поэтому мы можем объединить пользовательские методы проверки и существующие атрибуты проверки, однако мы хотим.
Пользовательские атрибуты проверки
Другой способ выполнения пользовательской проверки заключается в реализации пользовательского [ValidationAttribute]
и последующего вставки логики проверки в переопределенный IsValid
метод. Это обеспечивает дополнительную гибкость по сравнению с описанным выше подходом, так как это упрощает простое повторное использование одного и того же атрибута в нескольких местах.
Предположим, мы хотели проверить свойство на основе его относительного значения в отношении другого свойства в том же представлении. Первым шагом будет определение пользовательского типа [GreaterThanAttribute]
, например:
public sealed class GreaterThanAttribute : ValidationAttribute
{
public GreaterThanAttribute(string propertyName)
{
PropertyName = propertyName;
}
public string PropertyName { get; }
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
object
instance = validationContext.ObjectInstance,
otherValue = instance.GetType().GetProperty(PropertyName).GetValue(instance);
if (((IComparable)value).CompareTo(otherValue) > 0)
{
return ValidationResult.Success;
}
return new("The current value is smaller than the other one");
}
}
Далее мы можем добавить этот атрибут в наш viewmodel:
public class ComparableModel : ObservableValidator
{
private int a;
[Range(10, 100)]
[GreaterThan(nameof(B))]
public int A
{
get => this.a;
set => SetProperty(ref this.a, value, true);
}
private int b;
[Range(20, 80)]
public int B
{
get => this.b;
set
{
SetProperty(ref this.b, value, true);
ValidateProperty(A, nameof(A));
}
}
}
В этом случае у нас есть два числовых свойства, которые должны находиться в определенном диапазоне и с определенной связью между собой (A
должны быть больше B
). Мы добавили новое [GreaterThanAttribute]
над первым свойством, и мы также добавили вызов ValidateProperty
в метод B
задания для , чтобы A
проверяться снова при B
изменении (так как его состояние проверки зависит от него). Мы просто нуждаемся в этих двух строках кода в нашем представлении, чтобы включить эту настраиваемую проверку, и мы также получаем преимущество использования повторно используемых настраиваемых атрибутов проверки, которые также могут быть полезны в других представлениях в нашем приложении. Этот подход также помогает с модульной структурой кода, так как логика проверки теперь полностью отделяется от самого определения viewmodel.
Примеры
- Ознакомьтесь с примером приложения (для нескольких платформ пользовательского интерфейса), чтобы просмотреть набор средств MVVM в действии.
- Дополнительные примеры можно найти в модульных тестах.
MVVM Toolkit