Gibt es eine Möglichkeit, mehrere Wertkonverter in XAML zu verketten?

123

Ich habe eine Situation, in der ich einen ganzzahligen Wert anzeigen muss, der an eine Eigenschaft in meinem Datenkontext gebunden ist, nachdem ich zwei separate Konvertierungen durchgeführt habe:

  1. Kehren Sie den Wert innerhalb eines Bereichs um (z. B. liegt der Bereich zwischen 1 und 100; der Wert im Datenkontext beträgt 90; der Benutzer sieht den Wert 10).
  2. Konvertieren Sie die Zahl in eine Zeichenfolge

Mir ist klar, dass ich beide Schritte ausführen kann, indem ich meinen eigenen Konverter erstelle (der IValueConverter implementiert). Ich habe jedoch bereits einen separaten Wertekonverter, der nur den ersten Schritt ausführt, und der zweite Schritt wird von Int32Converter abgedeckt.

Gibt es eine Möglichkeit, diese beiden vorhandenen Klassen in XAML zu verketten, ohne eine weitere Klasse erstellen zu müssen, die sie aggregiert?

Wenn ich dies klären muss, lassen Sie es mich bitte wissen. :) :)

Vielen Dank.

Mal Ross
quelle

Antworten:

198

Ich habe diese Methode von Gareth Evans in meinem Silverlight-Projekt verwendet.

Hier ist meine Implementierung:

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
}

Welches kann dann in XAML wie folgt verwendet werden:

<c:ValueConverterGroup x:Key="InvertAndVisibilitate">
   <c:BooleanInverterConverter/>
   <c:BooleanToVisibilityConverter/>
</c:ValueConverterGroup>
Stadt, Dorf
quelle
3
Ist es für eine Implementierung von ConvertBack am besten, eine Kopie der Sammlung zu erstellen und diese umzukehren und dann darüber zu aggregieren? Also wäre das ConvertBackreturn this.Reverse<IValueConverter>().Aggregate(value, (current, converter) => converter.ConvertBack(current, targetType, parameter, culture));
Nick Udell
5
@ DLL Dies ist nicht wirklich elegant, da es nicht funktioniert. Es bietet allen Konvertern den endgültigen
Zieltyp
Wie kann ich dies mit einem MultiValueConverter als erstem Konverter verwenden?
LightMonk
1
@Town Ein Kollege hat diese Frage gerade gefunden und ich habe sie erneut nachgeschlagen, um der Nostalgie willen. Nur, ich habe gerade bemerkt, dass Sie nicht die Gutschrift erhalten haben, die Sie verdient haben (ich hatte meine eigene Antwort akzeptiert !), Also habe ich Ihre Antwort jetzt als akzeptiert markiert. Nur etwa 9 Jahre zu spät ...: Gesichtspalme:
Mal Ross
@ MalRoss Haha! Danke dir! Gut zu hören, dass es immer noch nützlich ist. Ich habe Silverlight seit ungefähr 8 Jahren nicht mehr berührt und dennoch ist dies immer noch meine beliebteste Antwort :)
Stadt
54

Mit freundlicher Genehmigung von Josh Smith: Piping Value Converters (Link archive.org) genau das gefunden, wonach ich gesucht habe .

Er definiert eine ValueConverterGroupKlasse, deren Verwendung in XAML genau so ist, wie ich es mir erhofft hatte. Hier ist ein Beispiel:

<!-- 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> 

Tolles Zeug. Danke, Josh. :) :)

Mal Ross
quelle
2
In dieser Lösung darf jeder Konverter nur einen Typ behandeln (er muss im Attribut single-ValueConversion deklariert sein). Die @ Town-Lösung kann auch mit Multikonvertern umgehen.
Y. Shoham
9
Bitte posten Sie die Implementierung; ansonsten Linkrot
Jake Berger
9

Die Implementierung des Silverlight-Projekts von Gareth Evans durch Town ist großartig, unterstützt jedoch keine unterschiedlichen Konverterparameter.

Ich habe es so geändert, dass Sie durch Kommas getrennte Parameter angeben können (es sei denn, Sie entkommen ihnen natürlich).

Konverter:

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;
    }
}

Hinweis: ConvertBack ist hier nicht implementiert. Die Vollversion finden Sie in meinem Gist .

Implementierung:

<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>
Trevi Awater
quelle
6

Ja, es gibt Möglichkeiten, Konverter zu verketten, aber es sieht nicht gut aus und Sie brauchen es hier nicht. Wenn Sie dies jemals brauchen, fragen Sie sich, ob dies wirklich der richtige Weg ist. Einfach funktioniert immer besser, auch wenn Sie Ihren eigenen Konverter schreiben müssen.

In Ihrem speziellen Fall müssen Sie lediglich einen konvertierten Wert in eine Zeichenfolge formatieren. StringFormatEigentum an einem Bindingist dein Freund hier.

 <TextBlock Text="{Binding Value,Converter={StaticResource myConverter},StringFormat=D}" />
wpfwannabe
quelle
5
Wenn Sie häufig Bindungen verwenden, führt das Schreiben von benutzerdefinierten Konvertern zu Kettenkonvertern zu einer Menge dummer Konverter für alle Arten von Konfigurationen. In diesem Fall ist die akzeptierte Antwort eine wunderbare Lösung.
Jacek Gorgoń
0

Hier ist eine kleine Erweiterung der Antwort von Town zur Unterstützung der Mehrfachbindung:

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
}
Aaron
quelle