WPF等のXAMLファミリーのBindingで、バインド元とバインド先の値の型が異なる場合、
値コンバータクラスを利用して変換をかける。
例えば、IsVisible みたいな名前のboolプロパティによって、trueになったら表示、falseになったら非表示というようなバインドをしたい場合は BooleanToVisibilityConverter を使うことになる。
BooleanToVisibilityConverterは標準で用意されているクラスだが、同様に、たとえばColor→Brushの変換をしたければ、
IValueConverterインタフェースを実装したColorToBrushConverterクラスを自作してやればよい。
[ValueConversion(typeof(Color), typeof(Brush))] public class ColorToBrushConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { if (value == null || !(value is Color)) return DependencyProperty.UnsetValue; var col = (Color)value; return new SolidColorBrush(col); } public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { var brush = value as SolidColorBrush; if (brush == null) return DependencyProperty.UnsetValue; return brush.Color; } }
では、たとえば string→Color→Brushのように、複数の変換をリレーしたい場合はどうするか。
話はかんたんで、StringToBrushConverterクラスを作って、
内部でStringToColorConverterとColorToBrushConverterを順に呼んでやればいい。
しかし、どうせなら、汎用的なリレーコンバートクラスを作っておきたいですね。
調べてみると、当然ながら先人がいる。
Piping Value Converters in WPF - CodeProject
しかし、アイデア自体は単純なので、まずはシンプルに自作してみよう。
先に、StringToColorConverterを作っておく。
「あか」「あお」「きいろ」の文字列だけ変換する雑なもの。
[ValueConversion(typeof(string), typeof(Color))] public class StringToColorConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { var str = value as string; if (str == null) return DependencyProperty.UnsetValue; switch (str) { case "あか": return Colors.Red; case "あお": return Colors.Blue; case "きいろ": return Colors.Yellow; default: return Colors.Black; } } public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { if (value == null || !(value is Color)) return DependencyProperty.UnsetValue; var col = (Color)value; return col == Colors.Red ? "あか" : col == Colors.Blue ? "あお" : col == Colors.Yellow ? "きいろ" : DependencyProperty.UnsetValue; } }
さてでは、このStringToColorConverterとColorToBrushConverterを連結するためのコンバータを作る。
[ContentProperty(nameof(Converters))] public class ValueConverterGroup : IValueConverter { public Collection<IValueConverter> Converters { get; } = new Collection<IValueConverter>(); public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { var result = value; if (Converters == null) return result; foreach (var conv in Converters) { result = conv.Convert(result, targetType, parameter, culture); } return result; } public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { var result = value; if (Converters == null) return result; foreach (var conv in Converters.Reverse()) { result = conv.ConvertBack(result, targetType, parameter, culture); } return result; } }
targetTypeのあたりがだいぶ不真面目ですね。
先ほどのリンク先のコードではもうちょっとちゃんとやってた。
まあともかく、↓こんなふうに使います。
<Window x:Class="ValueConverterPipeSample.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:ValueConverterPipeSample" Height="100" Width="300" FontSize="16"> <Window.Resources> <local:ValueConverterGroup x:Key="stringToBrushConverter"> <local:StringToColorConverter /> <local:ColorToBrushConverter /> </local:ValueConverterGroup> </Window.Resources> <StackPanel Orientation="Vertical"> <!--入力欄--> <TextBox Name="_textBox" /> <!--入力された文字列を文字色に反映して表示--> <TextBlock Text="「あか」と入れれば赤くなるよ" Foreground="{Binding ElementName=_textBox, Path=Text, Converter={StaticResource stringToBrushConverter}}"/> </StackPanel> </Window>
実行。
以上。