다음을 통해 공유


ObservableValidator

인터페이스 ObservableValidator 를 구현하는 INotifyDataErrorInfo 기본 클래스로, 다른 애플리케이션 모듈에 노출된 속성의 유효성 검사를 지원합니다. 또한 상속되므로 ObservableObject구현하고 INotifyPropertyChanging 구현합니다INotifyPropertyChanged. 속성 변경 알림과 속성 유효성 검사를 모두 지원해야 하는 모든 종류의 개체에 대한 시작점으로 사용할 수 있습니다.

플랫폼 API: ObservableValidator, ObservableObject

작동 방식

ObservableValidator 에는 다음과 같은 주요 기능이 있습니다.

  • 이벤트 및 기타 필요한 API를 노출하기 ErrorsChanged 위한 INotifyDataErrorInfo기본 구현을 제공합니다.
  • 기본 ObservableObject 클래스에서 제공하는 오버로드를 기반으로 하여 값을 업데이트하기 전에 속성의 유효성을 자동으로 검사하고 필요한 이벤트를 발생시키는 기능을 제공하는 일련의 추가 SetProperty 오버로드를 제공합니다.
  • 유효성 검사에 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);
    }
}

여기서는 해당 값이 업데이트될 ObservableValidator때 속성의 SetProperty<T>(ref T, T, bool, string) 유효성을 검사할 것임을 나타내기 위해 true 추가 bool 매개 변수 집합을 통해 노출되는 메서드를 호출합니다. ObservableValidator 는 속성에 적용된 특성과 함께 지정된 모든 검사를 사용하여 모든 새 값에 대한 유효성 검사를 자동으로 실행합니다. 그런 다음 다른 구성 요소(예: UI 컨트롤)는 viewmodel과 상호 작용하고 뷰모델에 현재 존재하는 오류를 반영하도록 해당 상태를 수정할 수 있습니다. 이 메서드를 등록하고 메서드를 ErrorsChanged 사용하여 GetErrors(string) 수정된 각 속성에 대한 오류 목록을 검색합니다.

사용자 지정 유효성 검사 메서드

속성의 유효성을 검사하려면 viewmodel이 추가 서비스, 데이터 또는 기타 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");
    }
}

이 경우 viewmodel에 삽입되는 서비스를 통해 속성에 대한 Name 유효성 검사를 수행하는 정적 ValidateName 메서드가 있습니다. 이 메서드는 name viewmodel 인스턴스, 유효성을 검사할 속성의 이름, 선택적으로 서비스 공급자 및 ValidationContext 사용하거나 설정할 수 있는 일부 사용자 지정 플래그와 같은 항목을 포함하는 사용 중인 인스턴스와 속성 값을 받습니다. 이 경우 유효성 검사 컨텍스트에서 인스턴스를 RegistrationForm 검색하고 여기에서 삽입된 서비스를 사용하여 속성의 유효성을 검사합니다. 이 유효성 검사는 다른 특성에 지정된 특성 옆에 실행되므로 사용자 지정 유효성 검사 메서드와 기존 유효성 검사 특성을 자유롭게 결합할 수 있습니다.

사용자 지정 유효성 검사 특성

사용자 지정 유효성 검사를 수행하는 또 다른 방법은 사용자 지정 [ValidationAttribute] 을 구현한 다음 재정의된 메서드에 유효성 검사 논리를 IsValid 삽입하는 것입니다. 이렇게 하면 여러 위치에서 동일한 특성을 쉽게 재사용할 수 있으므로 위에서 설명한 접근 방식에 비해 유연성을 더할 수 있습니다.

동일한 viewmodel의 다른 속성과 관련하여 상대적 값을 기반으로 속성의 유효성을 검사하려고 했습니다. 첫 번째 단계는 다음과 같이 사용자 지정 [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] 속성을 추가했으며 setter에 대한 B호출 ValidateProperty 도 추가했으므로 유효성 검사 상태가 따라 달라지므로 A 변경될 때마다 B 유효성이 다시 검사됩니다. 이 사용자 지정 유효성 검사를 사용하도록 설정하려면 viewmodel에서 이러한 두 줄의 코드만 있으면 되며, 애플리케이션의 다른 viewmodel에서도 유용할 수 있는 재사용 가능한 사용자 지정 유효성 검사 특성이 있다는 이점이 있습니다. 또한 이 방법은 유효성 검사 논리가 viewmodel 정의 자체에서 완전히 분리되므로 코드 모듈화에도 도움이 됩니다.

예제

  • 샘플 앱(여러 UI 프레임워크의 경우)을 확인하여 작동 중인 MVVM 도구 키트를 확인합니다.
  • 단위 테스트에서 더 많은 예제를 찾을 수도 있습니다.