ObservableValidator

Az ObservableValidator interfészt megvalósító INotifyDataErrorInfo alaposztály, amely támogatja a más alkalmazásmodulok számára elérhető tulajdonságok érvényesítését. Azt is örökli, ObservableObjectígy implementálja INotifyPropertyChanged és INotifyPropertyChanging is. Kiindulópontként használható minden olyan objektumhoz, amelyeknek támogatniuk kell a tulajdonságmódosítási értesítéseket és a tulajdonságérvényesítést.

Platform API-k:ObservableValidator, ObservableObject

Hogyan működik?

ObservableValidator a következő fő funkciókkal rendelkezik:

  • Alapszintű implementációt biztosít a(z) INotifyDataErrorInfo számára, elérhetővé téve a(z) ErrorsChanged eseményt és a többi szükséges API-t.
  • További SetProperty túlterhelések sorozatát biztosítja (az alaposztály ObservableObject által biztosítottakon felül), amelyek lehetővé teszi a tulajdonságok automatikus érvényesítését és a szükséges események növelését az értékek frissítése előtt.
  • Számos TrySetProperty túlterhelést biztosít, amelyek a SetProperty metódushoz hasonlóak, de csak akkor frissítik a célként megadott tulajdonságot, ha az érvényesítés sikeres, valamint visszaadják a létrehozott hibákat (ha vannak) további vizsgálat céljából.
  • Elérhetővé teszi a ValidateProperty metódust, amely hasznos lehet egy adott tulajdonság érvényesítésének manuális aktiválásához abban az esetben, ha az értéke nem frissült, de az érvényesítése egy másik, ehelyett frissített tulajdonság értékétől függ.
  • Elérhetővé teszi a ValidateAllProperties metódust, amely automatikusan végrehajtja az aktuális példány összes nyilvános példánytulajdonságának ellenőrzését, feltéve, hogy legalább egy [ValidationAttribute] van alkalmazva rájuk.
  • Olyan metódust ClearAllErrors tesz elérhetővé, amely hasznos lehet egy olyan űrlaphoz kötött modell alaphelyzetbe állításakor, amelyet a felhasználó esetleg újra ki szeretne tölteni.
  • Számos konstruktort kínál, amelyek lehetővé teszik különböző paraméterek átadását a ValidationContext tulajdonságok érvényesítéséhez használt példány inicializálásához. Ez különösen akkor lehet hasznos, ha olyan egyéni érvényesítési attribútumokat használ, amelyek további szolgáltatásokat vagy beállításokat igényelhetnek a megfelelő működéshez.

Egyszerű tulajdonság

Íme egy példa egy olyan tulajdonság implementálására, amely támogatja a változásértesítéseket és az ellenőrzést is:

public class RegistrationForm : ObservableValidator
{
    private string name;

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

Itt a(z) ObservableValidator által közzétett SetProperty<T>(ref T, T, bool, string) metódust hívjuk meg, és a további, true értékre állított bool paraméter azt jelzi, hogy a tulajdonságot akkor is ellenőrizni szeretnénk, amikor az értéke frissül. ObservableValidator automatikusan futtatja az ellenőrzést minden új értéken a tulajdonságra alkalmazott attribútumokkal megadott összes ellenőrzés használatával. A többi összetevő (például a felhasználói felület vezérlői) ezután kezelhetik a nézetmodellt, és módosíthatják az állapotukat, hogy azok tükrözzék a nézetmodellben jelenleg előforduló hibákat. Ehhez regisztrálni kell ErrorsChanged a metódust, és GetErrors(string) lekérheti az egyes módosított tulajdonságok hibáinak listáját.

Egyéni érvényesítési módszerek

Előfordulhat, hogy egy tulajdonság érvényesítéséhez egy nézetmodellnek további szolgáltatásokhoz, adatokhoz vagy egyéb API-khoz kell hozzáférése. A forgatókönyvtől és a szükséges rugalmassági szinttől függően különböző módokon adhat hozzá egyéni érvényesítést egy tulajdonsághoz. Íme egy példa arra, hogy a [CustomValidationAttribute] típus hogyan jelezheti, hogy egy adott metódust kell meghívni egy tulajdonság további érvényesítéséhez:

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

Ebben az esetben van egy statikus ValidateName módszerünk, amely egy olyan szolgáltatáson keresztül végez ellenőrzést a Name tulajdonságon, amelyet a nézetmodellbe injektálunk. Ez a metódus megkapja a name tulajdonság értékét és a ValidationContext használatban lévő példányt, amely olyan elemeket tartalmaz, mint a nézetmodell-példány, az érvényesítendő tulajdonság neve, valamint opcionálisan egy szolgáltató és néhány egyéni jelző, amelyet használhatunk vagy beállíthatunk. Ebben az esetben lekérjük a példányt az RegistrationForm érvényesítési környezetből, és onnan az injektált szolgáltatást használjuk a tulajdonság ellenőrzéséhez. Vegye figyelembe, hogy ez az ellenőrzés a többi attribútumban megadottak mellett lesz végrehajtva, így tetszés szerint kombinálhatjuk az egyéni érvényesítési módszereket és a meglévő érvényesítési attribútumokat.

Egyéni érvényesítési attribútumok

Az egyéni érvényesítés másik módja egy egyéni [ValidationAttribute] implementálása, majd az érvényesítési logika beillesztése a felülírt IsValid metódusba. Ez nagyobb rugalmasságot tesz lehetővé a fent leírt megközelítéshez képest, mivel nagyon egyszerűvé teszi ugyanazt az attribútumot több helyen is újra felhasználni.

Tegyük fel, hogy egy tulajdonságot a relatív értéke alapján szeretnénk érvényesíteni az ugyanazon a nézetmodellben lévő másik tulajdonságra vonatkozóan. Az első lépés egy egyéni [GreaterThanAttribute]beállítás definiálása, például:

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

Ezután hozzáadhatjuk ezt az attribútumot a nézetmodellhez:

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

Ebben az esetben két numerikus tulajdonságunk van, amelyeknek egy adott tartományban kell lenniük, és egy adott kapcsolattal kell rendelkezniük egymás között (A nagyobbnak kell lennie, mint B). Hozzáadtuk az új [GreaterThanAttribute] elemet az első tulajdonság fölé, és egy ValidateProperty hívást is hozzáadtunk a B setteréhez, hogy a(z) A újra érvényesítésre kerüljön, valahányszor a(z) B megváltozik (mivel az érvényességi állapota ettől függ). Az egyéni érvényesítés engedélyezéséhez csak erre a két kódsorra van szükségünk a nézetmodellben, és egy újrafelhasználható egyéni érvényesítési attribútummal is rendelkezünk, amely az alkalmazás más nézetmodelljeiben is hasznos lehet. Ez a megközelítés a kód modularizálásában is segít, mivel az érvényesítési logika most már teljesen leválasztva van magáról a nézetmodell-definícióról.

Examples

  • Tekintse meg a mintaalkalmazást (több felhasználói felületi keretrendszer esetén) az MVVM-eszközkészlet működés közbeni megtekintéséhez.
  • Az egységtesztekben további példákat is találhat.