Alessandro Del Sole's Blog

{ A programming space about Microsoft® .NET® }
posts - 1846, comments - 2047, trackbacks - 352

My Links

News

Your host

This is me! Questo spazio è dedicato a Microsoft® .NET®, di cui sono molto appassionato :-)

Cookie e Privacy

Disabilita cookie ShinyStat

Microsoft MVP

My MVP Profile

Microsoft Certified Professional

Microsoft Specialist

Il mio libro su VB 2015!

Pre-ordina il mio libro su VB 2015 Pre-ordina il mio libro "Visual Basic 2015 Unleashed". Clicca sulla copertina per informazioni!

Il mio libro su WPF 4.5.1!

Clicca sulla copertina per informazioni! E' uscito il mio libro "Programmare con WPF 4.5.1". Clicca sulla copertina per informazioni!

These postings are provided 'AS IS' for entertainment purposes only with absolutely no warranty expressed or implied and confer no rights.
If you're not an Italian user, please visit my English blog

Le vostre visite

I'm a VB!

Guarda la mia intervista a Seattle

Follow me on Twitter!

Altri spazi

CodePlex download Scarica i miei progetti open-source su CodePlex!

Article Categories

Archives

Post Categories

Image Galleries

Privacy Policy

giovedì 22 settembre 2016

Xamarin.Forms e la validazione dell'input lato client - parte 2

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 { setget; }
 
    public T TrueObject { setget; }
 
    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

posted @ giovedì 22 settembre 2016 20:07 | Feedback (0) | Filed Under [ Xamarin ]

Fai le tue domande a Scott Guthrie!

Come ormai sapete bene, il 7 ottobre Scott Guthrie parteciperà come ospite d'onore ai Community Days/Future Decoded e sarà speaker nella keynote dell'evento, e per l'occasione Microsoft Italia ha lanciato una bella iniziativa che vi consentirà di fare le vostre domande a Scott Guthrie tramite l'hashtag #CallScott su Twitter.


Quindi, twittate, fate le domande e includete ovviamente l'hashtag...

Alessandro

posted @ giovedì 22 settembre 2016 18:22 | Feedback (0) | Filed Under [ Community and friends ]

martedì 20 settembre 2016

Xamarin.Forms e la validazione dell'input lato client - prima parte

Premessa
Ho avuto la necessità di implementare la validazione dell'input dell'utente in un'app scritta con Xamarin.Forms. Per esempio, dire all'utente che un campo non può essere vuoto o più lungo di un certo numero di caratteri. L'obiettivo è quindi mostrare un messaggio all'utente per dire che qualcosa non va, all'interno della UI. In questa prima parte vi parlo dell'implementazione della validazione tramite behavior, nella seconda parte di come associarla alla UI e allo XAML.

Problema...
Ho molti difetti ma a cercare su Internet o sui libri me la cavo abbastanza bene e, a meno di sviste, la situazione di Xamarin.Forms (ieri e) oggi è la seguente:

  • Non esiste il concetto di ValidationRule come in WPF
  • Non esiste supporto a IDataErrorInfo
  • Non esiste supporto a INotifyDataErrorInfo
  • Non esistono ErrorTemplate
  • Le Data Annotations non hanno effetto sulla UI e richiedono diversi barbatrucchi per poter essere installate dal mitico NuGet. I progetti Windows 8.1 e Phone 8.1 non le supportano, bisogna toglierli, cambiare il target, installare la libreria.. ma comunque non hanno effetto.

Quindi avere una validazione nel nostro model non ha praticamente effetto sul data-binding. Il libro del grande Petzold parla di Trigger o di Behavior. Entrambi consentono di raggiungere l'obiettivo, ma i Trigger non hanno effetto in UWP e a me piace ragionare in modo universale (dove possibile).

In pratica, un Behavior è una classe che estende un controllo, anzi un oggetto che eredita da BindableObject, intercettando qualche comportamento e modificandone l'aspetto.

Ed è qui il punto: possiamo modificare l'aspetto di un controllo a seconda dell'input dell'utente, ma ogni logica di vera e propria validazione spetta a noi. E può starci, quindi rimbocchiamoci le maniche.

Obiettivo
Implementare due behavior, uno per impedire che una casella di testo sia vuota e uno per impedire che contenga più di 255 caratteri.

Implementazione: un'interfaccia e una classe base
Sul Web troverete tanti esempi, io ho voluto fare un passo in più ragionando per astrazione e riutilizzo di codice. Nel caso in esame, abbiamo l'obiettivo di due tipologie di validazione sullo stesso tipo di controllo, la Entry di Xamarin.Forms. Conviene quindi ragionare per astrazione e definire dapprima un'interfaccia come questa:

public interface IValidatorBehavior
{
    bool IsValid { getset; }
}
La proprietà IsValid ci servirà per determinare se il contenuto di una Entry è valido o meno. Ciò vuol dire che ogni behavior che valida una Entry dovrà avere questa proprietà, per cui conviene creare una classe base come la seguente, che eredita da Behavior<T> per forza di cose e che vuole che T sia un BindableObject. La proprietà è implementata come BindableProperty perché nella UI, come vedremo nella seconda parte, verrà utilizzata per fare binding di una Label al risultato del behavior. Ecco la classe:

public class BaseValidatorBehavior<T> : Behavior<T>,
    IValidatorBehavior where T : BindableObject
{
    static readonly BindablePropertyKey IsValidPropertyKey =
        BindableProperty.CreateReadOnly("IsValid"typeof(bool),
        typeof(BaseValidatorBehavior<T>), false);
 
    public static readonly BindableProperty IsValidProperty = IsValidPropertyKey.BindableProperty;
 
    public bool IsValid
    {
        get { return (bool)base.GetValue(IsValidProperty); }
        set { SetValue(IsValidPropertyKey, value); }
    }
}

Se avete un minimo di dimestichezza con le dependency property di WPF/UWP, qui il concetto è identico.

Implementazione: un behavior per la lunghezza massima
Di base un behavior ha un evento OnAttachedTo che riceve il controllo cui si applica e che a sua volta va a specificare un gestore per l'evento da intercettare, nel nostro caso il TextChanged della Entry. In questo secondo gestore, intercetteremo la stringa e vedremo se rispetta la logica. Non di meno, la lunghezza massima della stringa non sarà hard-coded, ma saremo noi (o chi per noi) a deciderla direttamente in binding e per questo implementeremo un'apposita BindableProperty come segue:

public class FieldLengthValidatorBehavior : BaseValidatorBehavior<Entry>
{
    public static readonly BindableProperty MaxLengthProperty =
        BindableProperty.Create("MaxLength"typeof(int),
            typeof(FieldLengthValidatorBehavior), 0);
 
    public int MaxLength
    {
        get { return (int)GetValue(MaxLengthProperty); }
        set { SetValue(MaxLengthProperty, value); }
    }
 
    protected override void OnAttachedTo(Entry bindable)
    {
        bindable.TextChanged += bindable_TextChanged;
    }
 
    private void bindable_TextChanged(object sender, TextChangedEventArgs e)
    {
 
        try
        {
            if (e.NewTextValue.Length > 0 && e.NewTextValue.Length > MaxLength)
            {
                IsValid = false;
                ((Entry)sender).Text = e.NewTextValue.Substring(0, MaxLength);
            }
            IsValid = true;
        }
        catch (Exception)
        {
 
 
        }
    }
 
    protected override void OnDetachingFrom(Entry bindable)
    {
        bindable.TextChanged -= bindable_TextChanged;
    }
}
Riepilogando:
  • il behavior eredita da quello base, prendendosi IsValid
  • il behavior si "attacca" alla Entry
  • ne ascolta l'evento TextChanged
  • verifica la lunghezza della stringa immessa e la confronta con la lunghezza massima fornita dallo sviluppatore nell'espressione di binding verso MaxLength. In questo caso non cambio il colore della Entry perché mostrerò il messaggio nella UI per varie altre ragioni, come vedremo nella seconda parte, ma potremmo farlo in fase di "analisi logica". Qui mi limito semplicemente a troncare la lunghezza del testo in base a quella specificata

E gli errori di validazione?

Saggia domanda. Nel primo behavior sto troncando la lunghezza della stringa, ma nel prossimo behavior, in cui valido il contenuto di un campo, devo fare diversamente. In sostanza devo avere un errore di validazione per ogni controllo a cui applicherò il behavior e consentire a tutti di conoscere lo stato degli errori. Questo perché potrei anche voler cambiare il behavior precedente. Una buona idea, soprattutto se pensiamo che gli oggetti da validare siano una manciata, è creare un Dictionary in questo modo:

public static class Validation
{
    /// <summary>
    /// A list of validation errors. Key of type object is typically the View
    /// while Value of type string is an error message
    /// </summary>
    public static Dictionary<objectstring> ValidationErrors { getset; }
}


Questa collection ci torna subito utile, per esempio nel behavior successivo il cui compito è quello di verificare che un campo non sia vuoto, modificando lo stato del placeholder della Entry.

public class FieldEmptyValidatorBehavior : BaseValidatorBehavior<Entry>
{
    protected override void OnAttachedTo(Entry bindable)
    {
        bindable.TextChanged += HandleTextChanged;
        if (Validation.ValidationErrors == nullValidation.ValidationErrors = new Dictionary<objectstring>();
        // TextChanged isn't raised at first time, so manually adding an error
        if (bindable.IsVisible == true)
            Validation.ValidationErrors.Add(bindable, "Field cannot be empty");
        else
        {
            if (Validation.ValidationErrors.Keys.Contains(bindable)) Validation.ValidationErrors.Remove(bindable);
        }
    }
 
    /// <summary>
    /// Perform the validation logic
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    void HandleTextChanged(object sender, TextChangedEventArgs e)
    {
        try
        {
            IsValid = !string.IsNullOrWhiteSpace(e.NewTextValue);
            var entry = (Entry)sender;
            if (IsValid)
            {
                entry.PlaceholderColor = Color.Default;
                entry.Placeholder = "";
                if (Validation.ValidationErrors.Keys.Contains(sender))
                {
                    Validation.ValidationErrors.Remove(sender);
                }
                return;
            }
            else
            {
                entry.PlaceholderColor = Color.Red;
                entry.Placeholder = "Field cannot be empty";
                if (Validation.ValidationErrors.Keys.Contains(sender))
                {
                    return;
                }
                else
                {
                    Validation.ValidationErrors.Add(sender, "Field cannot be empty");
                }
            }
        }
        catch (Exception)
        {
 
        }
    }
 
    protected override void OnDetachingFrom(Entry bindable)
    {
        bindable.TextChanged -= HandleTextChanged;
    }
}

Ogni altra logica di validazione andrebbe nel HandleTextChanged, anche se avrebbe poco senso colorare di rosso una casella vuota, a meno di voler personalizzare il bordo.

Ancora sulla collection di errori e considerazioni sulla validazione

Di fatto, l'attivazione o meno del behavior non impedisce ai dati di tornare alla sorgente in fase di data-binding. Il behavior è solo un'estensione del comportamento della UI. E' per tale ragione che abbiamo definito una collection che ci consente di memorizzare la presenza di un errore e che, alla sua verifica, consentirà alla UI di bloccare i passaggi successivi.
Come vedete nel secondo behavior, la prima volta devo scatenare a mano l'aggiunta di un errore alla collection perché il primo TextChanged non viene intercettato. Poi aggiungo errori quando il campo è vuoto, li rimuovo quando si digita e faccio un doppio controllo perché.. non si sa mai

La prossima volta vedremo come utilizzare questi behavior per mostrare all'utente che qualcosa non va. Ripeto "mostrare all'utente".

Alessandro

posted @ martedì 20 settembre 2016 22:00 | Feedback (0) | Filed Under [ Xamarin ]

lunedì 19 settembre 2016

Xamarin.Forms, il TimePicker e la selezione dell'ora

In Xamarin.Forms esiste il controllo TimePicker che serve a selezionare un'ora, di tipo TimeSpan. In alcuni casi potreste avere la necessità di intercettare un evento scatenato allo scegliere dell'ora sul controllo.

Vi aspettereste di trovare un SelectedTime, al pari del SelectedDate che esiste sul DatePicker ma.. Sopresa: non c'è  

A tutto c'è però soluzione, si può gestire l'evento PropertyChanged. Sebbene meno rimediato rispetto a qualcosa che inizi con Selected, va bene lo stesso. L'evento sul TimePicker si gestisce così:
private void OnTimePropertyChanged(object sender, PropertyChangedEventArgs e)
{
    if(e.PropertyName==TimePicker.TimeProperty.PropertyName)
    {
        // Dove sender è l'istanza del TimePicker
    }
}
Ovviamente cambierete il nome del gestore come meglio credete e lo assegnerete nello XAML sulla dichiarazione del controllo.

Alessandro

posted @ lunedì 19 settembre 2016 22:00 | Feedback (0) | Filed Under [ Xamarin ]

Generatore di badge per GitHub (e altro)

Se sfogliate spesso GitHub alla ricerca di progetti open source, avrete notato che spesso ci sono dei piccoli badge che mostrano lo stato del progetto, come lo stato di una release, lo stato delle build, la disponibilità su NuGet, ecc. ecc.



Tra i vari servizi che consentono di creare questi badge, ce n'è uno che si chiama Shields.io e che, a sua volta, ha origine da un progetto open source. C'è un elenco dimostrativo e in fondo alla pagina i campi per generare il proprio.

Se ne conoscete altri, ditelo... io intanto sto usando questo

Alessandro

posted @ lunedì 19 settembre 2016 10:54 | Feedback (0) | Filed Under [ Varie ]

domenica 18 settembre 2016

Desktop Bridge: portare le proprie applicazioni Win32 sullo Store di Windows 10

Forse ne avete sentito parlare ed ormai ci siamo. Project Centennial, ovvero Desktop Bridge e Desktop App Converter, con l'Anniversary Update di Windows 10 consentono di convertire un installer .exe o .msi in un'applicazione Universal Windows Platform, da poter poi "impacchettare" e distribuire sotto forma di file .appx che possa andare sullo Store di Windows 10.

Pensate: le vostre applicazioni WPF o Win32 nello Store, eccezionale!

Chiaramente, ci sono dei requisiti minimi che un'applicazione deve poter rispettare, e questo onestamente ci sta. L'elenco dei requisiti che la vostra applicazione deve soddisfare sono elencati in questa pagina.

La cosa bella è che un'applicazione desktop potrà sfruttare tipiche delle Store app ed è possibile semplificare il processo di conversione utilizzando un tool chiamato Desktop App Converter.

Per il momento vi lascio due link a due post sul blog del Team di MSDN Italia, scritti dall'amico Matteo Pagani e grande esperto della materia:


Poi vi chiedo un favore: soprattutto se siete iscritti a VB T&T, vorrei capire il livello di interesse che potreste avere nel portare la vostra app desktop sulla piattaforma Universal Windows, quindi dopo aver letto i requisiti minimi, scrivetemi pure a nome.cognome[@]visual-basic.it, così da capire se organizzare qualcosa al riguardo

Alessandro

posted @ domenica 18 settembre 2016 22:00 | Feedback (0) | Filed Under [ Windows Presentation Foundation Visual Studio 2015 Windows 10 ]

venerdì 16 settembre 2016

Future Decoded, Community Days 2016 e sessione su Visual Studio Code

Ormai lo sapete tutti, ne abbiamo parlato su Twitter, avete ricevuto newsletter, condiviso su Facebook e gli amici delle altre Community hanno fatto lo stesso perché l'occasione è ghiottissima

Quest'anno Microsoft ripropone l'evento Future Decoded, a Milano, il 6 e 7 ottobre. Senza nulla togliere a nessuno, la chicca ovviamente è la presenza di Scott Guthrie come speaker in keynote del secondo giorno.

L'abbinamento perfetto è la concomitanza con i Community Days, il più bello e importante evento community che si ripete ogni anno e che dovreste frequentare solo per l'aria che si respira!

Se poi nel passare di la voleste ascoltare il sottoscritto parlare un'oretta di Visual Studio Code, l'editor cross-platform per lo sviluppo di applicazioni su Linux, Mac e Windows... fatevi vedere il primo giorno alle 13 circa

Quest'anno poi ricorre il 10° anno, quindi un motivo in più per esserci, se non fisicamente anche in streaming. Tutti i dettagli sul sito.

L'hashtag su Twitter è #CDays16, non dimenticatelo

Alessandro

posted @ venerdì 16 settembre 2016 20:54 | Feedback (0) | Filed Under [ Community and friends ]

Sarò speaker a {Codemotion} Milano

Forse conoscete CodeMotion, un'importante conferenza che si tiene in tutto il mondo e che in Italia normalmente fa due tappe, Milano e Roma.

Per la prima volta avrò il piacere e l'onore di essere speaker in questa conferenza, nell'edizione milanese che si svolge dal 23 al 26 novembre 2016. Più precisamente il 25 novembre terrò una sessione su Roslyn e i compilatori open source C# e VB.

So già in partenza che sarà una bella esperienza perché l'ambiente e l'audience generale non sono gli stessi degli eventi cui normalmente partecipo, quindi avrò modo di confrontarmi con persone che non necessariamente si occupano di tecnologie Microsoft.

Se siete liberi e volete fare un salto, ci vediamo li

Alessandro

posted @ venerdì 16 settembre 2016 20:44 | Feedback (0) | Filed Under [ Community and friends .NET Compiler Platform (Roslyn) ]

Powered by:
Powered By Subtext Powered By ASP.NET