Share via


ObservableValidator

ObservableValidator è una classe di base che implementa l'interfacciaINotifyDataErrorInfo, fornendo il supporto per la convalida delle proprietà esposte ad altri moduli dell'applicazione. Eredita anche da ObservableObject, quindi implementa INotifyPropertyChanged e INotifyPropertyChanging . Può essere usato come punto di partenza per tutti i tipi di oggetti che devono supportare sia le notifiche di modifica delle proprietà che la convalida delle proprietà.

API della piattaforma:ObservableValidator, ObservableObject

Funzionamento

ObservableValidator presenta le funzionalità principali seguenti:

  • Fornisce un'implementazione di base per INotifyDataErrorInfo, esponendo l'evento ErrorsChanged e le altre API necessarie.
  • Fornisce una serie di overload aggiuntivi SetProperty (oltre a quelli forniti dalla classe base ObservableObject ), che offrono la possibilità di convalidare automaticamente le proprietà e generare gli eventi necessari prima di aggiornare i valori.
  • Espone un certo numero di TrySetProperty overload, simili a SetProperty ma con la possibilità di aggiornare la proprietà di destinazione solo se la convalida ha esito positivo e restituire gli errori generati (se presenti) per un'ulteriore ispezione.
  • Espone il ValidateProperty metodo , che può essere utile per attivare manualmente la convalida di una proprietà specifica nel caso in cui il relativo valore non sia stato aggiornato, ma la convalida dipende dal valore di un'altra proprietà che è stata invece aggiornata.
  • Espone il ValidateAllProperties metodo , che esegue automaticamente la convalida di tutte le proprietà dell'istanza pubblica nell'istanza corrente, purché ne abbia almeno uno [ValidationAttribute] applicato.
  • Espone un ClearAllErrors metodo che può essere utile durante la reimpostazione di un modello associato a un modulo che l'utente potrebbe voler compilare di nuovo.
  • Offre diversi costruttori che consentono di passare parametri diversi per inizializzare l'istanza ValidationContext che verrà usata per convalidare le proprietà. Ciò può essere particolarmente utile quando si usano attributi di convalida personalizzati che potrebbero richiedere servizi o opzioni aggiuntivi per funzionare correttamente.

Proprietà semplice

Ecco un esempio di come implementare una proprietà che supporta sia le notifiche di modifica che la convalida:

public class RegistrationForm : ObservableValidator
{
    private string name;

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

In questo caso viene chiamato il SetProperty<T>(ref T, T, bool, string) metodo esposto da ObservableValidatore il parametro aggiuntivo bool impostato su true indica che si vuole convalidare anche la proprietà quando viene aggiornato il relativo valore. ObservableValidator eseguirà automaticamente la convalida su ogni nuovo valore usando tutti i controlli specificati con gli attributi applicati alla proprietà . Altri componenti(ad esempio i controlli dell'interfaccia utente) possono quindi interagire con il modello di visualizzazione e modificarne lo stato in modo da riflettere gli errori attualmente presenti nel modello di visualizzazione, registrando ErrorsChanged e usando il GetErrors(string) metodo per recuperare l'elenco di errori per ogni proprietà modificata.

Metodi di convalida personalizzati

A volte la convalida di una proprietà richiede che un modello di visualizzazione abbia accesso a servizi, dati o altre API aggiuntive. Esistono diversi modi per aggiungere la convalida personalizzata a una proprietà, a seconda dello scenario e del livello di flessibilità necessario. Di seguito è riportato un esempio di come il [CustomValidationAttribute] tipo può essere usato per indicare che è necessario richiamare un metodo specifico per eseguire una convalida aggiuntiva di una proprietà:

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

In questo caso è disponibile un metodo statico ValidateName che eseguirà la convalida sulla Name proprietà tramite un servizio inserito nel modello di visualizzazione. Questo metodo riceve il name valore della proprietà e l'istanza ValidationContext in uso, che contiene elementi come l'istanza del modello di visualizzazione, il nome della proprietà da convalidare e, facoltativamente, un provider di servizi e alcuni flag personalizzati che è possibile usare o impostare. In questo caso, si sta recuperando l'istanza RegistrationForm dal contesto di convalida e da lì viene usato il servizio inserito per convalidare la proprietà. Si noti che questa convalida verrà eseguita accanto a quelle specificate negli altri attributi, quindi è possibile combinare metodi di convalida personalizzati e attributi di convalida esistenti, tuttavia.

Attributi di convalida personalizzati

Un altro modo per eseguire la convalida personalizzata consiste nell'implementare un oggetto personalizzato [ValidationAttribute] e quindi inserire la logica di convalida nel metodo sottoposto a IsValid override. Ciò consente una maggiore flessibilità rispetto all'approccio descritto in precedenza, in quanto rende molto semplice riutilizzare lo stesso attributo in più posizioni.

Si supponga di voler convalidare una proprietà in base al relativo valore relativo rispetto a un'altra proprietà nello stesso modello di visualizzazione. Il primo passaggio consiste nel definire un oggetto personalizzato [GreaterThanAttribute], come illustrato di seguito:

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

A questo punto è possibile aggiungere questo attributo al modello di visualizzazione:

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

In questo caso, sono presenti due proprietà numeriche che devono trovarsi in un intervallo specifico e con una relazione specifica tra loro (A deve essere maggiore di B). È stato aggiunto il nuovo [GreaterThanAttribute] oggetto sulla prima proprietà ed è stata aggiunta anche una chiamata a ValidateProperty nel setter per B, in modo che venga convalidata di nuovo ogni volta B che cambia (dal momento che A lo stato di convalida dipende da esso). Sono necessarie solo queste due righe di codice nel modello di visualizzazione per abilitare questa convalida personalizzata e si ottiene anche il vantaggio di avere un attributo di convalida personalizzata riutilizzabile che potrebbe essere utile anche in altri modelli di visualizzazione nell'applicazione. Questo approccio consente anche la modularizzazione del codice, poiché la logica di convalida è ora completamente disaccoppiata dalla definizione del modello di visualizzazione stessa.

Esempi