C'è un modo per concatenare più convertitori di valore in XAML?


123

Ho una situazione in cui devo mostrare un valore intero, associato a una proprietà sul mio contesto dati, dopo averlo sottoposto a due conversioni separate:

  1. Invertire il valore all'interno di un intervallo (ad esempio, l'intervallo è compreso tra 1 e 100; il valore nel contesto dati è 90; l'utente vede il valore 10)
  2. converte il numero in una stringa

Mi rendo conto che potrei fare entrambi i passaggi creando il mio convertitore (che implementa IValueConverter). Tuttavia, ho già un convertitore di valori separato che esegue solo il primo passaggio e il secondo passaggio è coperto da Int32Converter.

C'è un modo per concatenare queste due classi esistenti in XAML senza dover creare un'ulteriore classe che le aggreghi?

Se ho bisogno di chiarire qualcosa, per favore fatemelo sapere. :)

Grazie.

Risposte:


198

Ho usato questo metodo di Gareth Evans nel mio progetto Silverlight.

Ecco la mia implementazione:

public class ValueConverterGroup : List<IValueConverter>, IValueConverter
{
    #region IValueConverter Members

    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        return this.Aggregate(value, (current, converter) => converter.Convert(current, targetType, parameter, culture));
    }

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        throw new NotImplementedException();
    }

    #endregion
}

Che può quindi essere utilizzato in XAML in questo modo:

<c:ValueConverterGroup x:Key="InvertAndVisibilitate">
   <c:BooleanInverterConverter/>
   <c:BooleanToVisibilityConverter/>
</c:ValueConverterGroup>

3
È meglio, per un'implementazione di ConvertBack, creare una copia della raccolta e invertirla, quindi eseguire l'aggregazione su quella? Quindi il ConvertBack sarebbereturn this.Reverse<IValueConverter>().Aggregate(value, (current, converter) => converter.ConvertBack(current, targetType, parameter, culture));
Nick Udell

5
@DLeh Questo non è molto elegante perché non funziona. Fornisce a tutti i convertitori il tipo di target finale invece del tipo di target corretto ...
Aleksandar Toplek

Come posso usarlo con un MultiValueConverter come primo convertitore?
LightMonk

1
@Town Un collega ha appena trovato questa domanda e mi ha fatto cercare di nuovo, per amor di nostalgia. Solo, ho appena notato che non stavi ottenendo il credito che meritavi (avevo accettato la mia risposta!), Quindi ora ho contrassegnato la tua risposta come accettata. Solo circa 9 anni in ritardo ...: facepalm:
Mal Ross

@MalRoss Haha! Grazie! È bello sentire che è ancora utile, non ho toccato Silverlight ora per circa 8 di quegli anni eppure questa è ancora la mia risposta più popolare :)
Town

54

Ho trovato esattamente quello che stavo cercando, per gentile concessione di Josh Smith: Piping Value Converters (collegamento archivio.org) .

Definisce una ValueConverterGroupclasse, il cui utilizzo in XAML è esattamente come speravo. Ecco un esempio:

<!-- Converts the Status attribute text to a SolidColorBrush used to draw 
     the output of statusDisplayNameGroup. -->
<local:ValueConverterGroup x:Key="statusForegroundGroup">
  <local:IntegerStringToProcessingStateConverter  />
  <local:ProcessingStateToColorConverter />
  <local:ColorToSolidColorBrushConverter />
</local:ValueConverterGroup> 

Roba fantastica. Grazie, Josh. :)


2
In questa soluzione, ogni convertitore deve gestire un solo tipo (deve essere dichiarato nell'attributo ValueConversion singolo). La soluzione @Town può far fronte anche a multiconverter.
Y. Shoham

9
si prega di pubblicare l'implementazione; altrimenti, linkrot
Jake Berger

9

L'implementazione da parte di Town del progetto Silverlight di Gareth Evans è eccezionale, tuttavia non supporta diversi parametri di conversione.

L'ho modificato in modo da poter fornire i parametri, delimitati da virgole (a meno che non li esegua, ovviamente).

Converter:

public class ValueConverterGroup : List<IValueConverter>, IValueConverter
{
    private string[] _parameters;

    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        if(parameter != null)
            _parameters = Regex.Split(parameter.ToString(), @"(?<!\\),");

        return (this).Aggregate(value, (current, converter) => converter.Convert(current, targetType, GetParameter(converter), culture));
    }

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        throw new NotImplementedException();
    }

    private string GetParameter(IValueConverter converter)
    {
        if (_parameters == null)
            return null;

        var index = IndexOf(converter as IValueConverter);
        string parameter;

        try
        {
            parameter = _parameters[index];
        }

        catch (IndexOutOfRangeException ex)
        {
            parameter = null;
        }

        if (parameter != null)
            parameter = Regex.Unescape(parameter);

        return parameter;
    }
}

Nota: ConvertBack non è implementato qui, vedi il mio Gist per la versione completa.

Implementazione:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:converters="clr-namespace:ATXF.Converters;assembly=ATXF" x:Class="ATXF.TestPage">
  <ResourceDictionary>
    <converters:ValueConverterGroup x:Key="converters">
      <converters:ConverterOne />
      <converters:ConverterTwo />
    </converters:ValueConverterGroup>
  </ResourceDictionary>

  <Label Text="{Binding InitialValue, Converter={StaticResource converters}, ConverterParameter='Parameter1,Parameter2'}" />
</ContentPage>

6

Sì, ci sono modi per concatenare i convertitori, ma non sembra carino e non ne hai bisogno qui. Se mai ne avessi bisogno, chiediti se è davvero questa la strada da percorrere? Semplice funziona sempre meglio anche se devi scrivere il tuo convertitore.

Nel tuo caso particolare, tutto ciò che devi fare è formattare un valore convertito in una stringa. StringFormatla proprietà su un Bindingè tuo amico qui.

 <TextBlock Text="{Binding Value,Converter={StaticResource myConverter},StringFormat=D}" />

5
Se usi pesantemente i binding, scrivere convertitori personalizzati in convertitori a catena finisce con tonnellate di convertitori stupidi per tutti i tipi di configurazioni. In tal caso la risposta accettata è una soluzione meravigliosa.
Jacek Gorgoń

0

Ecco una piccola estensione della risposta di Town per supportare il multi-binding:

public class ValueConverterGroup : List<IValueConverter>, IValueConverter, IMultiValueConverter
{
    #region IValueConverter Members

    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        return this.Aggregate(value, (current, converter) => converter.Convert(current, targetType, parameter, culture));
    }

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        throw new NotImplementedException();
    }

    public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
    {
        return Convert(values as object, targetType, parameter, culture);
    }

    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }

    #endregion
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.