XAMLで複数の値コンバーターをチェーンする方法はありますか?


123

データコンテキストのプロパティにバインドされた整数値を2つの個別の変換にかけた後、それを表示する必要がある状況があります。

  1. 範囲内の値を反転します(例:範囲は1〜100、データコンテキストの値は90、ユーザーには10の値が表示されます)
  2. 数値を文字列に変換する

独自のコンバーター(IValueConverterを実装する)を作成することで、両方の手順を実行できることに気付きました。ただし、最初のステップのみを実行する別の値コンバーターが既にあり、2番目のステップはInt32Converterでカバーされています。

XAMLでこれらの2つの既存のクラスをチェーン化して、それらを集約するクラスをさらに作成する必要がない方法はありますか?

これを明確にする必要がある場合は、お知らせください。:)

ありがとう。

回答:


198

この方法は、SilverlightプロジェクトでGareth Evansが使用しました。

これが私の実装です:

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
}

その後、次のようにXAMLで使用できます。

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

3
ConvertBackの実装がコレクションのコピーを作成して元に戻し、それを集計するのが最善ですか?したがって、ConvertBackは次のようになりますreturn this.Reverse<IValueConverter>().Aggregate(value, (current, converter) => converter.ConvertBack(current, targetType, parameter, culture));
Nick Udell

5
@DLehこれは機能しないため、あまりエレガントではありません。すべてのコンバーターに正しいターゲットタイプの代わりに最終的なターゲットタイプを提供します...
Aleksandar Toplek

これをMultiValueConverterで最初のコンバータとして使用するにはどうすればよいですか?
LightMonk

1
@Town同僚がこの質問を見つけたので、懐かしさのためにもう一度調べました。ただ、あなたが必要なクレジットを受け取っいないことに気付いたので(自分の答えを受け入れました!)、今あなたの答えを受け入れ済みとしてマークしました。わずか約9年遅れ...:facepalm:
Mal Ross

@MalRossハハ!ありがとうございました!それがまだ有用であると聞いて良かった。私はそれらの年の約8のために今Silverlightに触れていませんが、これはまだ私の最も人気のある答えです:)
Town

54

Josh Smith:Piping Value Converters (archive.orgリンク)の厚意により、私が探していたものを正確に見つけました。

彼はValueConverterGroupクラスを定義します。XAMLでの使用は、まさに私が望んでいたとおりです。次に例を示します。

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

素晴らしいもの。ありがとう、ジョシュ。:)


2
このソリューションでは、各コンバーターは1つのタイプのみを処理する必要があります(single-ValueConversion-attributeで宣言する必要があります)。@Townソリューションはマルチコンバーターにも対応できます。
Y.ショーハム

9
実装を投稿してください。それ以外の場合、linkrot
Jake Berger

9

町の実装ギャレス・エバンスのSilverlightプロジェクトでは、それは別のコンバータのパラメータをサポートしていませんが、素晴らしいです。

パラメータをコンマ区切りで提供できるように変更しました(もちろん、エスケープしない限り)。

コンバータ:

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

注:ConvertBackは私の参照、ここでは実装されていません要旨のフルバージョンのために。

実装:

<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

はい、コンバーターをチェーンする方法はありますが、見た目が悪く、ここでは必要ありません。これが必要になった場合は、それが本当の道なのかと自問してください。Simpleは、独自のコンバーターを作成する必要がある場合でも、常により適切に機能します。

特定のケースでは、変換する値を文字列にフォーマットするだけです。StringFormatのプロパティBindingはここにあなたの友達です。

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

5
バインディングを多用する場合、チェーンコンバーターへのカスタムコンバーターを作成すると、あらゆる種類の構成で大量のダムコンバーターが作成されます。その場合、受け入れられた答えは素晴らしい解決策です。
JacekGorgoń

0

マルチバインディングをサポートするためのタウンの回答の小さな拡張は次のとおりです。

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
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.