Примечание.
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
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