Press "Enter" to skip to content

Uso dei Converter per dare qualità alla user interface

E’ un po’ di tempo che volevo scrivere alcune cose relative ai Converter, uno degli oggetti che preferisco per dare informazioni visuali e immediate agli utenti delle mie applicazioni.

L’occasione per scrivere finalmente un primo articolo ed un po’ di codice utile deriva da un post su stack overflow che mi ha dato una informazione importante per rendere più immediato l’uso dei converter. Lo trovate a questo indirizzo.

Innanzi tutto, per chi è ancora alle prime armi con WPF introduco che cosa sono i Converter. Un converter è una classe che implementa l’Interfaccia IValueConverter parte del namespace System.Windows.Data. Tale interfaccia contiene 2 metodi, Convert, ConvertBack. Nella maggior parte delle implementazioni, il metodo che ci servirà è il primo dei due.

Lo scopo di un converter è quello di convertire un dato di un certo tipo in un altro dato di tipo diverso. Più facile a farsi che a dirsi, nel caso del nostro codice esempio, il nostro converter ci permette di convertire un valore booleano quindi true o false in una immagine oppure in un testo.

Quale è lo scopo della conversione? Principalmente rendere dei dati non immediatamente e visivamente significativi per l’utente qualcosa di comprensibile in modo immediato.

Credo sia facile comprendere che False sia meno comprensibile di una immagine oppure una frase di senso compiuto.

un converter permette di creare un interfaccia come questa:

A mio avviso per un utente una colonna con un icona significativa (corredata di tooltip significativo) è più compatta e molto più facile da comprendere a colpo d’occhio di un testo o un codice alfanumerico o numerico. Nel caso qui sopra le icone indicano se un documento è stato stampato, trasmesso elettronicamente, esportato al gestionale, e se può essere ricalcolato.

Per Usare un converter all’interno dello XAML, nel modo tradizionale è necessario fare le seguenti operazioni:

<Window x:Class="UseOfConverters.MainWindow"
        xmlns:lconv="clr-namespace:UseOfConverters.Converters"

Per prima cosa bisogna dichiarare il namespace che contiene le classi converter all’interno del tag Window (o User control o Page).

<Window.Resources>
        <lconv:BoolToStopGoImageConverter x:Key="boolToStopGoImageConverter" />

Poi è necessario dichiarare il converter all’interno delle resources in modo da poterlo usare ed è indispensabile che la dichiarazione sia effettuata nel file XAML dove usiamo il converter.

            Source="{Binding Path=StopGo, UpdateSourceTrigger=PropertyChanged, Converter={StaticResource boolToStopGoImageConverter}}"

Infine il converter può essere assegnato ad una property di uno dei controlli all’interno dello XAML. Sono tre passaggi semplici ma un po’ noiosi soprattutto se abbiamo una window con molti converter, oppure se usiamo dei converter più volte all’interno della nostra applicazione.

Per questo motivo ho cercato di trovare un metodo più semplice per usare un converter e l’ho trovato nell’articolo a questo indirizzo.

In questo post su Stack overflow, viene spiegato un metodo per implementare il converter in modo da poter evitare la necessità di dichiarare ogni converter nelle Resources della window ed invece usarlo direttamente nel codice con l’ulteriore beneficio di avere anche l’intellisense.

In questo caso, l’uso del converter nello XAML è il seguente:

Source="{Binding Path=StopGo, UpdateSourceTrigger=PropertyChanged, Converter={lconv:BoolToStopGoImageConverter}}"

Vediamo ora come implementare il converter da Bool ad Immagine per poterlo utilizzare in questo modo.


La classe BoolToStopGoImageConverter

using System;
using System.Windows.Data;
using System.Globalization;
using System.Windows.Media.Imaging;
using System.Windows.Markup;

Per implementare il converter da Bool ad Immagine (BitmapImage per la precisione), dobbiamo per prima cosa accertarci che nella nostra classe siano indicati gli using per i namespace

  • System.Windows.Media.Imaging
  • System.Windows.Markup

Il primo ci permette di usare la classe BitmapImage e il secondo ci permette di implementare il “trucco” per semplificare l’uso del converter nelle nostre window.

[ValueConversion(typeof(bool), typeof(BitmapImage))]
public class BoolToStopGoImageConverter : MarkupExtensionIValueConverter
{
 

Nella dichiarazione della classe indichiamo specificamente i 2 tipi dati che utilizza il converter. inoltre indichiamo che la classe deriva da MarkupExtension ed implementa l’interfaccia IValueConverter.

MarkupExtension è ciò che implementa il trucco per poter usare direttamente il converter nello XAML, ci obbliga infatti ad implementare un metodo che permette allo XAML di istanziare il Converter.

private static BoolToStopGoImageConverter _converter = null;
public override object ProvideValue(IServiceProvider serviceProvider)
{
    if (_converter == null_converter = new BoolToStopGoImageConverter();
    return _converter;
}

Questa è l’implementazione che rende il converter direttamente istanziabile nello XAML senza passare per le Resources.

public object Convert(object valueType targetTypeobject parameterCultureInfo culture)
{
    BitmapImage result = new BitmapImage(new Uri("pack://application:,,,/Images/btn_064_692.png"));
    bool input = (bool)value;
    if (input)
    {
        result = new BitmapImage(new Uri("pack://application:,,,/Images/btn_064_636.png"));
    }
    return result;
}

Oltre all’implementazione del metodo ProvideValue, per realizzare il nostro converter dobbiamo generare il metodo Convert ed il metodo ConvertBack che sono previsti dall’interfaccia IValueConverter. In un converter come questo, che ha lo scopo di fornire un dato visuale il metodo ConvertBack non viene implementato, ma se dovessimo usarlo per fornire un dato (ad esempio su una textbox modificabile, dovremo implementare anche la conversione inversa, magari vedremo questo tipo di converter in un altro articolo. La funzione qui sopra, come potete vedere, verifica il valore passato a Convert dal Markup e Restituisce una diversa immagine in base al fatto che il valore sia True o False.

Vediamo come, allo stesso modo, ho implementato la conversione dei 2 valori del Boolean in due stringhe di testo potabili.

public object Convert(object valueType targetTypeobject parameterCultureInfo culture)
 {
     string result = "Now it is stopped (STOP)";
     bool input = (bool)value;
     if (input)
     {
         result = "Now it works (GO)";
     }
     return result;
 }

Nel codice a corredo trovate entrambi i converter. Oltre a questo, nel codice esempio, all’interno della Main Window trovate anche il semplice codice che permette di usare un converter all’interno del codice C#.

public string ConverterDescription
{
    get
    {
        //Chiamata da codice
        BoolToStopGoTooltipConverter conv = new BoolToStopGoTooltipConverter();
        CultureInfo info = CultureInfo.CreateSpecificCulture("it");
        return string.Format("In Code: {0}"conv.Convert(StopGo,typeof(string), nullinfo));
    }
 
}

Come potete vedere dal codice qui sopra, un converter può essere istanziato ed usato come una qualsiasi classe dei vostri programmi.

Il codice a corredo completo può essere scaricato all’indirizzo riportato qui sotto.

Per qualsiasi commento, domanda, o se trovate un errore non esitate ad usare il bottone con la bustina in cima alla pagina.