Sdílet prostřednictvím


ObservableValidator

Jedná se ObservableValidator o základní třídu implementující INotifyDataErrorInfo rozhraní, která poskytuje podporu pro ověřování vlastností vystavených v jiných aplikačních modulech. Také dědí z ObservableObject, takže implementuje INotifyPropertyChanged a INotifyPropertyChanging také. Dá se použít jako výchozí bod pro všechny druhy objektů, které potřebují podporovat oznámení o změnách vlastností i ověřování vlastností.

Rozhraní API platformy: ObservableValidator, ObservableObject

Jak to funguje

ObservableValidator má následující hlavní funkce:

  • Poskytuje základní implementaci pro INotifyDataErrorInfozveřejnění ErrorsChanged události a dalších nezbytných rozhraní API.
  • Poskytuje řadu dalších SetProperty přetížení (nad přetíženími poskytovanými základní ObservableObject třídou), které nabízejí možnost automatického ověřování vlastností a zvýšení potřebných událostí před aktualizací jejich hodnot.
  • Zveřejňuje řadu TrySetProperty přetížení, které jsou podobné SetProperty , ale s možností pouze aktualizovat cílovou vlastnost, pokud je ověření úspěšné, a vrátit vygenerované chyby (pokud existuje) pro další kontrolu.
  • Zveřejňuje metodu ValidateProperty , která může být užitečná k ruční aktivaci ověření konkrétní vlastnosti v případě, že jeho hodnota nebyla aktualizována, ale jeho ověření závisí na hodnotě jiné vlastnosti, která byla aktualizována.
  • Zpřístupňuje metodu ValidateAllProperties , která automaticky provede ověření všech vlastností veřejné instance v aktuální instanci za předpokladu, že mají alespoň jednu [ValidationAttribute] použitou.
  • Zpřístupňuje metodu ClearAllErrors , která může být užitečná při resetování modelu vázaného na nějaký formulář, který může uživatel chtít znovu vyplnit.
  • Nabízí řadu konstruktorů, které umožňují předání různých parametrů pro inicializaci ValidationContext instance, která se použije k ověření vlastností. To může být zvlášť užitečné při použití vlastních ověřovacích atributů, které můžou vyžadovat správné fungování dalších služeb nebo možností.

Jednoduchá vlastnost

Tady je příklad implementace vlastnosti, která podporuje oznámení o změnách i ověřování:

public class RegistrationForm : ObservableValidator
{
    private string name;

    [Required]
    [MinLength(2)]
    [MaxLength(100)]
    public string Name
    {
        get => name;
        set => SetProperty(ref name, value, true);
    }
}

Zde voláme metodu SetProperty<T>(ref T, T, bool, string) vystavenou metodou ObservableValidatora že další bool sada true parametrů indikuje, že chceme také ověřit vlastnost při aktualizaci její hodnoty. ObservableValidator automaticky spustí ověření pro každou novou hodnotu pomocí všech kontrol zadaných s atributy použitými u vlastnosti. Ostatní komponenty (například ovládací prvky uživatelského rozhraní) pak můžou pracovat s modelem viewmodel a upravit jejich stav tak, aby odrážely chyby, které jsou aktuálně přítomné v modelu viewmodel, tím, že se zaregistrují do ErrorsChanged a pomocí GetErrors(string) metody načtou seznam chyb pro každou upravenou vlastnost.

Vlastní metody ověřování

Ověření vlastnosti někdy vyžaduje, aby model zobrazení měl přístup k dalším službám, datům nebo jiným rozhraním API. Existují různé způsoby přidání vlastního ověření do vlastnosti v závislosti na scénáři a požadované úrovni flexibility. Tady je příklad, jak [CustomValidationAttribute] lze typ použít k označení, že je potřeba vyvolat konkrétní metodu, aby bylo možné provést dodatečné ověření vlastnosti:

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

V tomto případě máme statickou ValidateName metodu, která provede ověření Name vlastnosti prostřednictvím služby vložené do našeho modelu viewmodel. Tato metoda obdrží name hodnotu vlastnosti a ValidationContext instanci, která obsahuje například instanci viewmodel, název vlastnosti, která se ověřuje, a volitelně poskytovatele služeb a některé vlastní příznaky, které můžeme použít nebo nastavit. V tomto případě načítáme RegistrationForm instanci z kontextu ověření a tam používáme vloženou službu k ověření vlastnosti. Všimněte si, že toto ověření se provede vedle těch, které jsou uvedené v dalších atributech, takže můžeme kombinovat vlastní metody ověřování a existující ověřovací atributy, které se nám líbí.

Vlastní ověřovací atributy

Dalším způsobem, jak provést vlastní ověřování, je implementace vlastní [ValidationAttribute] a následné vložení logiky ověření do přepsáné IsValid metody. To umožňuje větší flexibilitu v porovnání s výše popsaným přístupem, protože je velmi snadné jednoduše znovu použít stejný atribut na více místech.

Předpokládejme, že jsme chtěli ověřit vlastnost na základě její relativní hodnoty s ohledem na jinou vlastnost ve stejném modelu viewmodel. Prvním krokem by bylo definovat vlastní , [GreaterThanAttribute]například takto:

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

Dále můžeme tento atribut přidat do našeho modelu 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));
        }
    }
}

V tomto případě máme dvě číselné vlastnosti, které musí být v určitém rozsahu a s určitým vztahem mezi sebou (A musí být větší než B). Přidali jsme nový [GreaterThanAttribute] přes první vlastnost a také jsme přidali volání do ValidateProperty setter for B, takže se A ověří znovu při každé B změně (protože jeho stav ověření závisí na něm). Potřebujeme tyto dva řádky kódu v našem modelu viewmodel, abychom umožnili toto vlastní ověřování, a také získáme výhodu opětovného použití vlastního ověřovacího atributu, který by mohl být užitečný i v jiných modelech zobrazení v naší aplikaci. Tento přístup také pomáhá s modularizací kódu, protože logika ověřování je nyní zcela oddělená od samotné definice modelu viewmodel.

Příklady

  • Podívejte se na ukázkovou aplikaci (pro více architektur uživatelského rozhraní) a podívejte se na sadu nástrojů MVVM v akci.
  • Další příklady najdete také v testech jednotek.