数値を配色や文字列に変換する Converter を作りましたが、必要な値に応じて Converter を用意するのは少し面倒です
というわけで、変換後の値を任意の型にできるような汎用的な Converter にしてみたいと思います
まずは Converter を作成
/// <summary> /// 数値から任意の値に変換する Converter /// </summary> [ContentProperty("Ranges")] public class RangeToValueConverter<T> : IValueConverter { /// <summary> /// ColorTypeConverter /// </summary> private static readonly ColorTypeConverter ColorConverter = new ColorTypeConverter(); /// <summary> /// ThicknessTypeConverter /// </summary> private static readonly ThicknessTypeConverter ThicknessConverter = new ThicknessTypeConverter(); /// <summary> /// 数値範囲定義 /// </summary> public List<RangeToValue> Ranges { get; set; } /// <summary> /// コンストラクタ /// </summary> public RangeToValueConverter() { this.Ranges = new List<RangeToValue>(); } /// <summary> /// 数値から任意の値に変換します /// </summary> /// <param name="value">数値</param> /// <param name="targetType">対象の型</param> /// <param name="parameter">パラメータ</param> /// <param name="culture">カルチャ</param> /// <returns>任意の値</returns> public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { var converted = default(T); var number = default(double); if (value is double) { number = (double)value; } else if (value != null) { double.TryParse(value.ToString(), out number); } var range = (from r in this.Ranges where r.From <= number && r.To >= number select r).FirstOrDefault(); if (range == null) { return converted; } try { switch (typeof(T).Name.ToLower()) { case "color": if (ColorConverter.CanConvertFrom(range.Value.GetType())) { converted = (T)ColorConverter.ConvertFrom(culture, range.Value); } break; case "thickness": if (ThicknessConverter.CanConvertFrom(range.Value.GetType())) { converted = (T)ThicknessConverter.ConvertFrom(culture, range.Value); } break; default: converted = (T)range.Value; break; } } catch (InvalidCastException) { } return converted; } public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { throw new NotImplementedException(); } } /// <summary> /// 数値範囲の記述クラス /// </summary> public class RangeToValue : BindableObject { /// <summary> /// 数値範囲の最小値 /// </summary> public double From { get; set; } /// <summary> /// 数値範囲の最大値 /// </summary> public double To { get; set; } /// <summary> /// 変換後の値 /// </summary> public object Value { get; set; } /// <summary> /// コンストラクタ /// </summary> public RangeToValue() { this.From = 0d; this.To = 0d; this.Value = null; } }
クラスをジェネリック型にして任意の値を XAML 側から指定できるようにしています
とりあえず文字列から変換しなければならなさそうな Color と Thickness のみ特別に分岐して変換します
これを前回の XAML を少し変更して利用します
<?xml version="1.0" encoding="utf-8" ?> <ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:b="clr-namespace:XamarinControl.Behaviors;assembly=XamarinControl" xmlns:c="clr-namespace:XamarinControl.Controls;assembly=XamarinControl" xmlns:cv="clr-namespace:XamarinControl.Converters;assembly=XamarinControl" xmlns:s="clr-namespace:XamarinControl.Selectors;assembly=XamarinControl" xmlns:vm="clr-namespace:XamarinControl.ViewModels;assembly=XamarinControl" xmlns:scg="clr-namespace:System.Collections.Generic;assembly=mscorlib" x:Class="XamarinControl.Views.TopPage"> <ContentPage.Resources> <ResourceDictionary> <cv:RangeToValueConverter x:Key="RangeToColorConverter" x:TypeArguments="Color"> <cv:RangeToValue From="0" To="60" Value="Red"/> <cv:RangeToValue From="60" To="80" Value="Green"/> <cv:RangeToValue From="80" To="100" Value="Blue"/> </cv:RangeToValueConverter> <cv:RangeToValueConverter x:Key="RangeToTextConverter" x:TypeArguments="x:String"> <cv:RangeToValue From="0" To="60" Value="赤点です!"/> <cv:RangeToValue From="80" To="100" Value="合格!"/> </cv:RangeToValueConverter> </ResourceDictionary> </ContentPage.Resources> <ListView > <ListView.ItemsSource> <scg:List x:TypeArguments="x:Int32"> <x:Int32>50</x:Int32> <x:Int32>70</x:Int32> <x:Int32>80</x:Int32> <x:Int32>100</x:Int32> </scg:List> </ListView.ItemsSource> <ListView.ItemTemplate> <DataTemplate> <ViewCell> <StackLayout Orientation="Horizontal" Padding="20,10" Spacing="10"> <Label Text="{Binding StringFormat='{0} 点'}" HorizontalOptions="Center" VerticalOptions="Center" TextColor="{Binding Converter={StaticResource RangeToColorConverter}}" FontSize="50"/> <Label Text="{Binding Converter={StaticResource RangeToTextConverter}}" HorizontalOptions="Center" VerticalOptions="Center" FontSize="40"/> </StackLayout> </ViewCell> </DataTemplate> </ListView.ItemTemplate> </ListView> </ContentPage>
XAML 側からジェネリックの型(T の部分)を指定するには x:TypeArguments の添付プロパティを使います
Converter の定義以外は前回と全く同じ!
結果も全く同じ!
これで 2つ用意していた Converter が1つで済みましたね!