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
INotifyDataErrorInfo
zveř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 ObservableValidator
a ž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.