Nella prima parte abbiamo implementato dei behavior da utilizzare per validare l'input dell'utente, ma quella è l'infrastruttura che ancora non abbiamo visto in azione, perché da collegare alla UI e quindi allo XAML.
L'obiettivo è mostrare anche una Label di colore rosso, oltre alla logica applicata dal behavior sulle Entry. Per farlo devo convertire lo stato del behavior in un oggetto bool per capire se lo stato del controllo è valido oppure no e, conseguentemente, mostrare o nascondere la label. Mi serve quindi un converter, tipo di oggetto caro a chi conosce XAML e che si implementa sempre attraverso l'interfaccia IValueConverter, che in questa piattaforma è esposta dal namespace Xamarin.Forms. Eccolo:
public class BoolToObjectConverter<T> : IValueConverter
{
public T FalseObject { set; get; }
public T TrueObject { set; get; }
public object Convert(object value, Type targetType,
object parameter, CultureInfo culture)
{
return (bool)value ? this.TrueObject : this.FalseObject;
}
public object ConvertBack(object value, Type targetType,
object parameter, CultureInfo culture)
{
return ((T)value).Equals(this.TrueObject);
}
}
Successivamente, nello XAML della pagina in cui intendiamo utilizzare i behavior, definiremo 3 stili per il controllo Label. Il primo, uno stile base che stabilisce corsivo e font small. Due stili, poi, che stabiliscono come la Label deve apparire a seconda che la proprietà IsValid nei behavior sia vera oppure falsa, quindi se la Label in questione deve o meno essere visibile. Eccoli:
<Style x:Key="baseStyle" TargetType="Label">
<Setter Property="FontSize" Value="Small" />
<Setter Property="FontAttributes" Value="Italic" />
</Style>
<converters:BoolToObjectConverter x:Key="boolToStringEmpty"
x:TypeArguments="Style">
<converters:BoolToObjectConverter.FalseObject>
<Style TargetType="Label" BasedOn="{StaticResource baseStyle}">
<Setter Property="TextColor" Value="Red" />
<Setter Property="Text" Value="Field cannot be empty" />
</Style>
</converters:BoolToObjectConverter.FalseObject>
<converters:BoolToObjectConverter.TrueObject>
<Style TargetType="Label" BasedOn="{StaticResource baseStyle}">
<Setter Property="TextColor" Value="Default" />
<Setter Property="Text" Value="" />
</Style>
</converters:BoolToObjectConverter.TrueObject>
</converters:BoolToObjectConverter>
Nota: in Xamarin.Forms 2.3.0.49 è stato introdotto il supporto ai Resource dictionary che prima non c'era, quindi può essere eventualmente utile da tenere a mente.
Ottimo, a questo punto ho tutto quello che mi serve. Dichiaro un namespace XML per fare riferimento alla classe che definisce il converter e uno per fare riferimento al namespace che definisce i behavior, e poi vado ad utilizzare i behavior così:
<ContentPage.Resources>
<ResourceDictionary MergedWith="styles:SharedResources" />
</ContentPage.Resources>
<StackLayout VerticalOptions="Center" HorizontalOptions="Center">
<Entry Text="{Binding Name, Mode=TwoWay}" WidthRequest="250">
<Entry.Behaviors>
<behaviors:FieldEmptyValidatorBehavior x:Name="StringEmptyValidator" />
<behaviors:FieldLengthValidatorBehavior MaxLength="255" />
</Entry.Behaviors>
</Entry>
<Label Style="{Binding Source={x:Reference StringEmptyValidator},
Path=IsValid,
Converter={StaticResource boolToStringEmpty}}" />
</StackLayout>
La Entry ha una collection chiamata Behaviors, all'interno della quale metto tutte le istanze dei behavior che voglio usare. Notate come il primo abbia anche un nome, grazie al quale la Label sottostante può fare binding per conoscere lo stato della proprietà IsValid e per applicare il relativo stile a seconda del suo valore.
Alla fine di tutto, il risultato è questo:

La Label rossa sparirà quando digiteremo del testo all'interno della Entry e se tenteremo di scrivere più di 255 caratteri, la stringa verrà tagliata.
Gli errori vengono aggiunti e rimossi al Dictionary definito la volta scorsa, che controlleremo prima di fare altro anche perché, come detto, questa è più una forma di presentazione nella UI di un problema, più che una vera validazione dei dati. Ma tant'è.
Alessandro