Condividi tramite


Convalida nelle app aziendali

Nota

Questo eBook è stato pubblicato nella primavera del 2017 e non è stato aggiornato da allora. C'è molto nel libro che rimane prezioso, ma alcuni dei materiali sono obsoleti.

Qualsiasi app che accetta input dagli utenti deve assicurarsi che l'input sia valido. Un'app può, ad esempio, verificare la presenza di input che contiene solo caratteri in un intervallo specifico, è di una determinata lunghezza o corrisponde a un formato specifico. Senza convalida, un utente può fornire dati che causano l'esito negativo dell'app. La convalida applica regole business e impedisce a un utente malintenzionato di inserire dati dannosi.

Nel contesto del modello Model-View-ViewModel (MVVM), è spesso necessario un modello di visualizzazione o un modello per eseguire la convalida dei dati e segnalare eventuali errori di convalida alla visualizzazione in modo che l'utente possa correggerli. L'app per dispositivi mobili eShopOnContainers esegue la convalida sincrona lato client delle proprietà del modello di visualizzazione e notifica all'utente di eventuali errori di convalida evidenziando il controllo contenente i dati non validi e visualizzando messaggi di errore che informano l'utente del motivo per cui i dati non sono validi. La figura 6-1 mostra le classi coinvolte nell'esecuzione della convalida nell'app per dispositivi mobili eShopOnContainers.

Validation classes in the eShopOnContainers mobile app

Figura 6-1: Classi di convalida nell'app per dispositivi mobili eShopOnContainers

Visualizzare le proprietà del modello che richiedono la convalida sono di tipo ValidatableObject<T> e ogni istanza di ValidatableObject<T> ha regole di convalida aggiunte alla relativa proprietà Validations. La convalida viene richiamata dal modello di visualizzazione chiamando il metodo Validate dell'istanza di ValidatableObject<T>, che recupera le regole di convalida e le esegue sulla proprietà ValidatableObject<T>Value. Eventuali errori di convalida vengono inseriti nella Errors proprietà dell'istanza ValidatableObject<T> e la IsValid proprietà dell'istanza viene aggiornata per indicare se la ValidatableObject<T> convalida ha avuto esito positivo o negativo.

La notifica di modifica delle proprietà viene fornita dalla classe ExtendedBindableObject e pertanto un controllo Entry può essere associato alla proprietà IsValid dell'istanza di ValidatableObject<T> nella classe del modello di visualizzazione per ricevere una notifica relativa alla validità o meno dei dati immessi.

Specifica delle regole di convalida

Le regole di convalida vengono specificate creando una classe che deriva dall'interfaccia IValidationRule<T>, illustrata nell'esempio di codice seguente:

public interface IValidationRule<T>  
{  
    string ValidationMessage { get; set; }  
    bool Check(T value);  
}

Questa interfaccia specifica che una classe di regole di convalida deve fornire un booleanCheck metodo utilizzato per eseguire la convalida richiesta e una ValidationMessage proprietà il cui valore è il messaggio di errore di convalida che verrà visualizzato se la convalida non riesce.

L'esempio di codice seguente illustra la IsNotNullOrEmptyRule<T> regola di convalida, usata per eseguire la convalida del nome utente e della password immessi dall'utente in LoginView quando si usano servizi fittizi nell'app per dispositivi mobili eShopOnContainers:

public class IsNotNullOrEmptyRule<T> : IValidationRule<T>  
{  
    public string ValidationMessage { get; set; }  

    public bool Check(T value)  
    {  
        if (value == null)  
        {  
            return false;  
        }  

        var str = value as string;  
        return !string.IsNullOrWhiteSpace(str);  
    }  
}

Il Check metodo restituisce un boolean valore che indica se l'argomento value è null, vuoto o è costituito solo da spazi vuoti.

Anche se non usato dall'app per dispositivi mobili eShopOnContainers, l'esempio di codice seguente mostra una regola di convalida per la convalida degli indirizzi di posta elettronica:

public class EmailRule<T> : IValidationRule<T>  
{  
    public string ValidationMessage { get; set; }  

    public bool Check(T value)  
    {  
        if (value == null)  
        {  
            return false;  
        }  

        var str = value as string;  
        Regex regex = new Regex(@"^([\w\.\-]+)@([\w\-]+)((\.(\w){2,3})+)$");  
        Match match = regex.Match(str);  

        return match.Success;  
    }  
}

Il Check metodo restituisce un boolean valore che indica se l'argomento value è un indirizzo di posta elettronica valido. A tale scopo, cercare l'argomento valore per la prima occorrenza del criterio di espressione regolare specificato nel costruttore Regex. Se il criterio di espressione regolare è stato trovato nella stringa di input può essere determinato controllando il valore della Match proprietà dell'oggetto Success .

Nota

La convalida delle proprietà può talvolta comportare proprietà dipendenti. Un esempio di proprietà dipendenti è quando il set di valori validi per la proprietà A dipende dal valore specifico impostato nella proprietà B. Per verificare che il valore della proprietà A sia uno dei valori consentiti comporta il recupero del valore della proprietà B. Inoltre, quando il valore della proprietà B cambia, la proprietà A deve essere riconvalidata.

Aggiunta di regole di convalida a una proprietà

Nell'app per dispositivi mobili eShopOnContainers visualizzare le proprietà del modello che richiedono la convalida sono dichiarate come di tipo ValidatableObject<T>, dove T è il tipo dei dati da convalidare. Nell'esempio di codice seguente viene illustrato un esempio di due proprietà di questo tipo:

public ValidatableObject<string> UserName  
{  
    get  
    {  
        return _userName;  
    }  
    set  
    {  
        _userName = value;  
        RaisePropertyChanged(() => UserName);  
    }  
}  

public ValidatableObject<string> Password  
{  
    get  
    {  
        return _password;  
    }  
    set  
    {  
        _password = value;  
        RaisePropertyChanged(() => Password);  
    }  
}

Affinché si verifichi la convalida, è necessario aggiungere regole di convalida alla Validations raccolta di ogni ValidatableObject<T> istanza, come illustrato nell'esempio di codice seguente:

private void AddValidations()  
{  
    _userName.Validations.Add(new IsNotNullOrEmptyRule<string>   
    {   
        ValidationMessage = "A username is required."   
    });  
    _password.Validations.Add(new IsNotNullOrEmptyRule<string>   
    {   
        ValidationMessage = "A password is required."   
    });  
}

Questo metodo aggiunge la regola di convalida IsNotNullOrEmptyRule<T> all'insieme Validations di ogni istanza di ValidatableObject<T>, specificando i valori per la proprietà ValidationMessage della regola di convalida, che specifica il messaggio di errore di convalida che verrà visualizzato se la convalida non riesce.

Attivazione della convalida

L'approccio di convalida usato nell'app per dispositivi mobili eShopOnContainers può attivare manualmente la convalida di una proprietà e attivare automaticamente la convalida quando una proprietà cambia.

Attivazione manuale della convalida

La convalida può essere attivata manualmente per una proprietà del modello di visualizzazione. Ad esempio, questo si verifica nell'app per dispositivi mobili eShopOnContainers quando l'utente tocca il pulsante Login in LoginView, quando si usano servizi fittizi. Il delegato del comando chiama il metodo MockSignInAsync in LoginViewModel, che richiama la convalida eseguendo il metodo Validate, illustrato nell'esempio di codice seguente:

private bool Validate()  
{  
    bool isValidUser = ValidateUserName();  
    bool isValidPassword = ValidatePassword();  
    return isValidUser && isValidPassword;  
}  

private bool ValidateUserName()  
{  
    return _userName.Validate();  
}  

private bool ValidatePassword()  
{  
    return _password.Validate();  
}

Il Validate metodo esegue la convalida del nome utente e della password immessi dall'utente in LoginView, richiamando il metodo Validate in ogni ValidatableObject<T> istanza. Nell'esempio di codice seguente viene illustrato il metodo Validate dalla ValidatableObject<T> classe :

public bool Validate()  
{  
    Errors.Clear();  

    IEnumerable<string> errors = _validations  
        .Where(v => !v.Check(Value))  
        .Select(v => v.ValidationMessage);  

    Errors = errors.ToList();  
    IsValid = !Errors.Any();  

    return this.IsValid;  
}

Questo metodo cancella l'insieme Errors e quindi recupera tutte le regole di convalida aggiunte alla raccolta dell'oggetto Validations . Viene eseguito il metodo Check per ogni regola di convalida recuperata e il valore della proprietà ValidationMessage per qualsiasi regola di convalida che non riesce a convalidare i dati viene aggiunto alla raccolta Errors dell'istanza di ValidatableObject<T>. Infine, la proprietà IsValid viene impostata e il relativo valore viene restituito al metodo chiamante, che indica se la convalida è riuscita o non è riuscita.

Attivazione della convalida quando cambiano le proprietà

La convalida può essere attivata anche ogni volta che viene modificata una proprietà associata. Ad esempio, quando un'associazione bidirezionale in LoginView imposta la proprietà UserName o Password, viene attivata la convalida. Nell'esempio di codice seguente viene illustrato come si verifica questa situazione:

<Entry Text="{Binding UserName.Value, Mode=TwoWay}">  
    <Entry.Behaviors>  
        <behaviors:EventToCommandBehavior  
            EventName="TextChanged"  
            Command="{Binding ValidateUserNameCommand}" />  
    </Entry.Behaviors>  
    ...  
</Entry>

Il controllo Entry viene associato alla proprietà UserName.Value dell'istanza di ValidatableObject<T> e all'insieme Behaviors del controllo è stata aggiunta un'istanza di EventToCommandBehavior. Questo comportamento esegue in ValidateUserNameCommand risposta all'evento [TextChanged] generato su Entry, generato quando il testo nelle Entry modifiche viene generato. A sua volta, il delegato ValidateUserNameCommand esegue il metodo ValidateUserName, che esegue il metodo Validate sull'istanza di ValidatableObject<T>. Pertanto, ogni volta che l'utente immette un carattere nel controllo Entry per il nome utente, viene eseguita la convalida dei dati immessi.

Per altre informazioni sui comportamenti, vedere Implementazione di comportamenti.

Visualizzazione degli errori di convalida

L'app per dispositivi mobili eShopOnContainers notifica all'utente eventuali errori di convalida evidenziando il controllo che contiene i dati non validi con una riga rossa e visualizzando un messaggio di errore che informa l'utente del motivo per cui i dati non sono validi sotto il controllo contenente i dati non validi. Quando i dati non validi vengono corretti, la riga diventa nera e il messaggio di errore viene rimosso. La figura 6-2 mostra LoginView nell'app per dispositivi mobili eShopOnContainers quando sono presenti errori di convalida.

Displaying validation errors during login

Figura 6-2: Visualizzazione di errori di convalida durante l'accesso

Evidenziazione di un controllo contenente dati non validi

Il LineColorBehavior comportamento associato viene usato per evidenziare Entry i controlli in cui si sono verificati errori di convalida. Nell'esempio di codice seguente viene illustrato come il LineColorBehavior comportamento associato è associato a un Entry controllo :

<Entry Text="{Binding UserName.Value, Mode=TwoWay}">
    <Entry.Style>
        <OnPlatform x:TypeArguments="Style">
            <On Platform="iOS, Android" Value="{StaticResource EntryStyle}" />
            <On Platform="UWP" Value="{StaticResource UwpEntryStyle}" />
        </OnPlatform>
    </Entry.Style>
    ...
</Entry>

Il Entry controllo utilizza uno stile esplicito, illustrato nell'esempio di codice seguente:

<Style x:Key="EntryStyle"  
       TargetType="{x:Type Entry}">  
    ...  
    <Setter Property="behaviors:LineColorBehavior.ApplyLineColor"  
            Value="True" />  
    <Setter Property="behaviors:LineColorBehavior.LineColor"  
            Value="{StaticResource BlackColor}" />  
    ...  
</Style>

Questo stile imposta le ApplyLineColor proprietà associate e LineColor del LineColorBehavior comportamento associato nel Entry controllo . Per altre informazioni sugli stili, vedere Stili.

Quando il valore della ApplyLineColor proprietà associata viene impostato o modificato, il LineColorBehavior comportamento associato esegue il OnApplyLineColorChanged metodo , illustrato nell'esempio di codice seguente:

public static class LineColorBehavior  
{  
    ...  
    private static void OnApplyLineColorChanged(  
                BindableObject bindable, object oldValue, object newValue)  
    {  
        var view = bindable as View;  
        if (view == null)  
        {  
            return;  
        }  

        bool hasLine = (bool)newValue;  
        if (hasLine)  
        {  
            view.Effects.Add(new EntryLineColorEffect());  
        }  
        else  
        {  
            var entryLineColorEffectToRemove =   
                    view.Effects.FirstOrDefault(e => e is EntryLineColorEffect);  
            if (entryLineColorEffectToRemove != null)  
            {  
                view.Effects.Remove(entryLineColorEffectToRemove);  
            }  
        }  
    }  
}

I parametri per questo metodo forniscono l'istanza del controllo a cui è associato il comportamento e i valori precedenti e nuovi della ApplyLineColor proprietà associata. La EntryLineColorEffect classe viene aggiunta all'insieme del Effects controllo se la ApplyLineColor proprietà associata è true, in caso contrario viene rimossa dalla raccolta del Effects controllo. Per altre informazioni sui comportamenti, vedere Implementazione di comportamenti.

La EntryLineColorEffect sottoclasse della RoutingEffect classe e viene illustrata nell'esempio di codice seguente:

public class EntryLineColorEffect : RoutingEffect  
{  
    public EntryLineColorEffect() : base("eShopOnContainers.EntryLineColorEffect")  
    {  
    }  
}

La RoutingEffect classe rappresenta un effetto indipendente dalla piattaforma che esegue il wrapping di un effetto interno specifico della piattaforma. Ciò semplifica il processo di rimozione dell'effetto, poiché non è previsto l'accesso alle informazioni sul tipo in fase di compilazione per un effetto specifico della piattaforma. Chiama EntryLineColorEffect il costruttore della classe base, passando un parametro costituito da una concatenazione del nome del gruppo di risoluzione e dall'ID univoco specificato in ogni classe di effetti specifica della piattaforma.

L'esempio di codice seguente illustra l'implementazione eShopOnContainers.EntryLineColorEffect per iOS:

[assembly: ResolutionGroupName("eShopOnContainers")]  
[assembly: ExportEffect(typeof(EntryLineColorEffect), "EntryLineColorEffect")]  
namespace eShopOnContainers.iOS.Effects  
{  
    public class EntryLineColorEffect : PlatformEffect  
    {  
        UITextField control;  

        protected override void OnAttached()  
        {  
            try  
            {  
                control = Control as UITextField;  
                UpdateLineColor();  
            }  
            catch (Exception ex)  
            {  
                Console.WriteLine("Can't set property on attached control. Error: ", ex.Message);  
            }  
        }  

        protected override void OnDetached()  
        {  
            control = null;  
        }  

        protected override void OnElementPropertyChanged(PropertyChangedEventArgs args)  
        {  
            base.OnElementPropertyChanged(args);  

            if (args.PropertyName == LineColorBehavior.LineColorProperty.PropertyName ||  
                args.PropertyName == "Height")  
            {  
                Initialize();  
                UpdateLineColor();  
            }  
        }  

        private void Initialize()  
        {  
            var entry = Element as Entry;  
            if (entry != null)  
            {  
                Control.Bounds = new CGRect(0, 0, entry.Width, entry.Height);  
            }  
        }  

        private void UpdateLineColor()  
        {  
            BorderLineLayer lineLayer = control.Layer.Sublayers.OfType<BorderLineLayer>()  
                                                             .FirstOrDefault();  

            if (lineLayer == null)  
            {  
                lineLayer = new BorderLineLayer();  
                lineLayer.MasksToBounds = true;  
                lineLayer.BorderWidth = 1.0f;  
                control.Layer.AddSublayer(lineLayer);  
                control.BorderStyle = UITextBorderStyle.None;  
            }  

            lineLayer.Frame = new CGRect(0f, Control.Frame.Height-1f, Control.Bounds.Width, 1f);  
            lineLayer.BorderColor = LineColorBehavior.GetLineColor(Element).ToCGColor();  
            control.TintColor = control.TextColor;  
        }  

        private class BorderLineLayer : CALayer  
        {  
        }  
    }  
}

Il OnAttached metodo recupera il controllo nativo per il Xamarin.FormsEntry controllo e aggiorna il colore della linea chiamando il UpdateLineColor metodo . L'override OnElementPropertyChanged risponde alle modifiche alle proprietà associabili nel Entry controllo aggiornando il colore della linea se la proprietà associata LineColor cambia o la Height proprietà delle Entry modifiche. Per altre informazioni sugli effetti, vedere Effetti.

Quando nel controllo vengono immessi Entry dati validi, verrà applicata una linea nera alla fine del controllo per indicare che non è presente alcun errore di convalida. La figura 6-3 mostra un esempio di questo.

Black line indicating no validation error

Figura 6-3: Riga nera che indica nessun errore di convalida

Il Entry controllo dispone inoltre di un oggetto DataTrigger aggiunto alla relativa Triggers raccolta. L'esempio di codice seguente illustra :DataTrigger

<Entry Text="{Binding UserName.Value, Mode=TwoWay}">  
    ...  
    <Entry.Triggers>  
        <DataTrigger   
            TargetType="Entry"  
            Binding="{Binding UserName.IsValid}"  
            Value="False">  
            <Setter Property="behaviors:LineColorBehavior.LineColor"   
                    Value="{StaticResource ErrorColor}" />  
        </DataTrigger>  
    </Entry.Triggers>  
</Entry>

Questo DataTrigger monitora la UserName.IsValid proprietà e, se il valore diventa false, esegue , Setterche modifica la LineColor proprietà associata del LineColorBehavior comportamento associato in rosso. La figura 6-4 mostra un esempio di questo.

Red line indicating validation error

Figura 6-4: Linea rossa che indica l'errore di convalida

La riga nel Entry controllo rimarrà rossa mentre i dati immessi non sono validi, altrimenti cambierà in nero per indicare che i dati immessi sono validi.

Per altre informazioni sui trigger, vedere Trigger.

Visualizzazione dei messaggi di errore

L'interfaccia utente visualizza i messaggi di errore di convalida nei controlli Etichetta sotto ogni controllo i cui dati non sono stati convalidati. L'esempio di codice seguente mostra che Label visualizza un messaggio di errore di convalida se l'utente non ha immesso un nome utente valido:

<Label Text="{Binding UserName.Errors, Converter={StaticResource FirstValidationErrorConverter}}"  
       Style="{StaticResource ValidationErrorLabelStyle}" />

Ogni Label oggetto viene associato alla Errors proprietà dell'oggetto modello di visualizzazione da convalidare. La proprietà Errors viene fornita dalla classe ValidatableObject<T> ed è di tipo List<string>. Poiché la proprietà Errors può contenere più errori di convalida, l'istanza di FirstValidationErrorConverter viene usata per recuperare il primo errore dalla raccolta per la visualizzazione.

Riepilogo

L'app per dispositivi mobili eShopOnContainers esegue la convalida sincrona lato client delle proprietà del modello di visualizzazione e notifica all'utente di eventuali errori di convalida evidenziando il controllo contenente i dati non validi e visualizzando messaggi di errore che informano l'utente del motivo per cui i dati non sono validi.

Visualizzare le proprietà del modello che richiedono la convalida sono di tipo ValidatableObject<T> e ogni istanza di ValidatableObject<T> ha regole di convalida aggiunte alla relativa proprietà Validations. La convalida viene richiamata dal modello di visualizzazione chiamando il metodo Validate dell'istanza di ValidatableObject<T>, che recupera le regole di convalida e le esegue sulla proprietà ValidatableObject<T>Value. Eventuali errori di convalida vengono inseriti nella Errors proprietà dell'istanza ValidatableObject<T>e la IsValid proprietà dell'istanza viene aggiornata per indicare se la ValidatableObject<T> convalida ha avuto esito positivo o negativo.