バインディングの値コンバーター

Browse sample. サンプルを参照する

.NET Multi-platform App UI (.NET MAUI) データ バインディングでは通常、ソース プロパティからターゲット プロパティへ、または、場合によってはターゲット プロパティからソース プロパティへデータを転送します。 ソース プロパティとターゲット プロパティの型が同じ場合、または、暗黙の型変換によって一方の型がもう一方の型に変換できる場合は、この転送はすみやかに進みます。 それ以外の場合は、型変換を行う必要があります。

文字列の書式設定に関する記事では、データ バインディングの StringFormat プロパティを使用して任意の型を文字列に変換する方法について説明しました。 それ以外の型の変換では、IValueConverter インターフェイスを実装するクラス内に専用のコードを記述する必要があります IValueConverter を実装するクラスは、値コンバーターと呼ばれますが、バインディング コンバーターまたはバインディング値コンバーターと呼ばれることもよくあります。

バインディングの値コンバーター

ソース プロパティの型は int だが、ターゲット プロパティは bool の場合のデータ バインディングの定義を検討します。 このデータ バインディングでは、整数のソースが 0 に等しい場合に false を、それ以外の場合は true を生成する予定です。 これを行うには、IValueConverter インターフェイスを実装するクラスを使用します。

public class IntToBoolConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        return (int)value != 0;
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        return (bool)value ? 1 : 0;
    }
}

次に、このクラスのインスタンスを Binding クラスの Converter プロパティまたは Binding マークアップ拡張の Converter プロパティに設定します。 このクラスは、データ バインディングの一部になります。

OneWay または TwoWay バインディング内でソースからターゲットにデータを移行するときに、Convert メソッドが呼び出されます。 value パラメーターは、データ バインディング ソースからのオブジェクトまたは値になります。 メソッドは、データ バインディング ターゲットの型の値を返す必要があります。 ここに示したメソッドでは、value パラメーターを int にキャストして、その値を bool 戻り値の 0 と比較しています。

TwoWay または OneWayToSource バインディング内でターゲットから ースにデータを移行するときに、ConvertBack メソッドが呼び出されます。 ConvertBack は逆の変換を実行します。value パラメーターがターゲットからの bool であり、その値をソースの int 戻り値に変換することを想定しています。

Note

データ バインディングに StringFormat 設定も含まれている場合、結果が文字列として書式設定される前に、値コンバーターが呼び出されます。

次の例では、データ バインディングでこの値コンバーターを使用する方法を示します。

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:local="clr-namespace:DataBindingDemos"
             x:Class="DataBindingDemos.EnableButtonsPage"
             Title="Enable Buttons">
    <ContentPage.Resources>
        <local:IntToBoolConverter x:Key="intToBool" />
    </ContentPage.Resources>

    <StackLayout Padding="10, 0">
        <Entry x:Name="entry1"
               Text=""
               Placeholder="enter search term"
               VerticalOptions="Center" />
        <Button Text="Search"
                HorizontalOptions="Center"
                VerticalOptions="Center"
                IsEnabled="{Binding Source={x:Reference entry1},
                                    Path=Text.Length,
                                    Converter={StaticResource intToBool}}" />
        <Entry x:Name="entry2"
               Text=""
               Placeholder="enter destination"
               VerticalOptions="Center" />
        <Button Text="Submit"
                HorizontalOptions="Center"
                VerticalOptions="Center"
                IsEnabled="{Binding Source={x:Reference entry2},
                                    Path=Text.Length,
                                    Converter={StaticResource intToBool}}" />
    </StackLayout>
</ContentPage>

この例では、IntToBoolConverter をページのリソース ディクショナリ内でインスタンス化しています。 次に、StaticResource マークアップ拡張を使用して参照し、2 つのデータ バインディングで Converter プロパティを設定します。 ページ上の複数のデータ バインディング間でデータ コンバーターを共有することは、極めて一般的です アプリケーションの複数のページで値コンバーターが使用されている場合は、アプリケーション レベルのリソース ディクショナリ内でインスタンス化できます。

この例は、ユーザーが Entry ビューに入力したテキストに基づいて Button が操作を実行する場合の一般的なニーズを示しています。 Text プロパティの既定値は null であり、その場合はデータ バインディングが機能しないため、各 EntryText プロパティは空の文字列として初期化されます。 Entry に何も入力されなかった場合は、Button を無効にする必要があります。 各 Button は、IsEnabled プロパティ上にデータ バインディングを含みます。 データ バインディング ソースは、対応する EntryText プロパティの Length プロパティです。 その Length プロパティが 0 ではない場合、値コンバーターは true を返し、Button は有効になります。

Enable buttons.

Note

値コンバーターが OneWay バインディング内のみで使用されることがわかっている場合、ConvertBack メソッドは単純に null を返すことができます。

上記の Convert メソッドでは、暗黙的に value 引数の型は int であり、戻り値の型は bool であることを想定しています。 同様に、ConvertBack メソッドでは、value 引数の型は bool であり、戻り値は int であることを想定しています。 これに該当しない場合、ランタイム例外が発生します。

より汎用化されるように、また、複数の異なるデータ型を許可するように、値コンバーターを記述できます。 Convert および ConvertBack メソッドでは、value パラメーターと共に as または is 演算子を使用できます。または、このパラメーター上で GetType を呼び出して、値の型を特定してから、適切な処理を実行することが可能です。 各メソッドの戻り値で想定される型は、targetType パラメーターによって指定されます。 値コンバーターは、さまざまなターゲット型のデータ バインディングと共に使用される場合があります。 この場合、値コンバーターは targetType 引数を使用して正しい型への変換を実行できます。

別のカルチャに対しては実行される変換が異なる場合には、この目的のために culture パラメーターを使用します。

バインディング コンバーター プロパティ

値コンバーターのクラスには、プロパティとジェネリック パラメーターを含めることができます。 次の値コンバーターは、ソースからターゲットの T 型オブジェクトに bool を変換します。

public class BoolToObjectConverter<T> : IValueConverter
{
    public T TrueObject { get; set; }
    public T FalseObject { get; set; }

    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        return (bool)value ? TrueObject : FalseObject;
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        return ((T)value).Equals(TrueObject);
    }
}

次の例では、このコンバーターを使用して Switch ビューの値を表示する方法を示します。 一般的にはリソース ディクショナリ内でリソースとして値コンバーターをインスタンス化しますが、この例では別の方法を示しています。 ここでは、各値コンバーターが Binding.Converter プロパティ要素タグ間でインスタンス化されています。 x:TypeArguments は汎用の引数を示し、TrueObject および FalseObject は両方とも、その型のオブジェクトに設定されます。

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:local="clr-namespace:DataBindingDemos"
             x:Class="DataBindingDemos.SwitchIndicatorsPage"
             Title="Switch Indicators">
    <ContentPage.Resources>
        <Style TargetType="Label">
            <Setter Property="FontSize" Value="18" />
            <Setter Property="VerticalOptions" Value="Center" />
        </Style>

        <Style TargetType="Switch">
            <Setter Property="VerticalOptions" Value="Center" />
        </Style>
    </ContentPage.Resources>

    <StackLayout Padding="10, 0">
        <StackLayout Orientation="Horizontal"
                     VerticalOptions="Center">
            <Label Text="Subscribe?" />
            <Switch x:Name="switch1" />
            <Label>
                <Label.Text>
                    <Binding Source="{x:Reference switch1}"
                             Path="IsToggled">
                        <Binding.Converter>
                            <local:BoolToObjectConverter x:TypeArguments="x:String"
                                                         TrueObject="Of course!"
                                                         FalseObject="No way!" />
                        </Binding.Converter>
                    </Binding>
                </Label.Text>
            </Label>
        </StackLayout>

        <StackLayout Orientation="Horizontal"
                     VerticalOptions="Center">
            <Label Text="Allow popups?" />
            <Switch x:Name="switch2" />
            <Label>
                <Label.Text>
                    <Binding Source="{x:Reference switch2}"
                             Path="IsToggled">
                        <Binding.Converter>
                            <local:BoolToObjectConverter x:TypeArguments="x:String"
                                                         TrueObject="Yes"
                                                         FalseObject="No" />
                        </Binding.Converter>
                    </Binding>
                </Label.Text>
                <Label.TextColor>
                    <Binding Source="{x:Reference switch2}"
                             Path="IsToggled">
                        <Binding.Converter>
                            <local:BoolToObjectConverter x:TypeArguments="Color"
                                                         TrueObject="Green"
                                                         FalseObject="Red" />
                        </Binding.Converter>
                    </Binding>
                </Label.TextColor>
            </Label>
        </StackLayout>

        <StackLayout Orientation="Horizontal"
                     VerticalOptions="Center">
            <Label Text="Learn more?" />
            <Switch x:Name="switch3" />
            <Label FontSize="18"
                   VerticalOptions="Center">
                <Label.Style>
                    <Binding Source="{x:Reference switch3}"
                             Path="IsToggled">
                        <Binding.Converter>
                            <local:BoolToObjectConverter x:TypeArguments="Style">
                                <local:BoolToObjectConverter.TrueObject>
                                    <Style TargetType="Label">
                                        <Setter Property="Text" Value="Indubitably!" />
                                        <Setter Property="FontAttributes" Value="Italic, Bold" />
                                        <Setter Property="TextColor" Value="Green" />
                                    </Style>
                                </local:BoolToObjectConverter.TrueObject>

                                <local:BoolToObjectConverter.FalseObject>
                                    <Style TargetType="Label">
                                        <Setter Property="Text" Value="Maybe later" />
                                        <Setter Property="FontAttributes" Value="None" />
                                        <Setter Property="TextColor" Value="Red" />
                                    </Style>
                                </local:BoolToObjectConverter.FalseObject>
                            </local:BoolToObjectConverter>
                        </Binding.Converter>
                    </Binding>
                </Label.Style>
            </Label>
        </StackLayout>
    </StackLayout>
</ContentPage>

この例では、最後の 3 つの SwitchLabel のペアで、汎用の引数が Style に設定され、Style オブジェクト全体が TrueObjectFalseObject の値に指定されています。 これらは、リソース ディクショナリ内の Label 設定の暗黙的な形式をオーバーライドしているので、その形式のプロパティが Label に明示的に割り当てられます。 Switch を切り替えると、対応する Label に変更が反映されます。

Switch indicators.

Note

トリガーを使用して、他のビューに基づいてユーザー インターフェイスに変更を実装することもできます。 詳細については、トリガーを参照してください。

バインディング コンバーター パラメーター

Binding クラスは ConverterParameter プロパティを定義し、Binding マークアップ拡張も ConverterParameter プロパティを定義します。 このプロパティが設定された場合、値は Convert および ConvertBack メソッドに parameter 引数として渡されます。 複数のデータ バインディング間で値コンバーターのインスタンスが共有されている場合でも、異なる変換を実行するために、ConverterParameter は異なる可能性があります。

色選択のプログラムを利用して、ConverterParameter プロパティの使用例を示します。 この例では、RgbColorViewModel には、Color 値を構成するために使用される RedGreenBlue という名前の float 型の 3 つのプロパティがあることを示しています。

public class RgbColorViewModel : INotifyPropertyChanged
{
    Color color;
    string name;

    public event PropertyChangedEventHandler PropertyChanged;

    public float Red
    {
        get { return color.Red; }
        set
        {
            if (color.Red != value)
            {
                Color = new Color(value, color.Green, color.Blue);
            }
        }
    }

    public float Green
    {
        get { return color.Green; }
        set
        {
            if (color.Green != value)
            {
                Color = new Color(color.Red, value, color.Blue);
            }
        }
    }

    public float Blue
    {
        get { return color.Blue; }
        set
        {
            if (color.Blue != value)
            {
                Color = new Color(color.Red, color.Green, value);
            }
        }
    }

    public Color Color
    {
        get { return color; }
        set
        {
            if (color != value)
            {
                color = value;
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Red"));
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Green"));
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Blue"));
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Color"));

                Name = NamedColor.GetNearestColorName(color);
            }
        }
    }

    public string Name
    {
        get { return name; }
        private set
        {
            if (name != value)
            {
                name = value;
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Name"));
            }
        }
    }
}

RedGreenBlue プロパティ値の範囲は 0 から 1 の間になります。 ただし、コンポーネントが 2 桁の 16 進数値として表示される方が望ましい場合があります。 これらを XAML 内で 16 進数値として表示するには、255 で乗算し、整数に変換してから、StringFormat プロパティの "X2" の仕様を使って書式設定する必要があります。 255 で乗算し、整数に変換するには、値コンバーターを使用します。 値コンバーターをできる限り汎用化するために、乗算の係数は ConverterParameter プロパティを使って指定できます。つまり、Convert および ConvertBack メソッドを parameter 引数として入力することになります。

public class FloatToIntConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        return (int)Math.Round((float)value * GetParameter(parameter));
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        return (int)value / GetParameter(parameter);
    }

    double GetParameter(object parameter)
    {
        if (parameter is float)
            return (float)parameter;
        else if (parameter is int)
            return (int)parameter;
        else if (parameter is string)
            return float.Parse((string)parameter);

        return 1;
    }
}

この例では、Convert メソッドは parameter 値を乗算しながら float から int に変換します。 ConvertBack メソッドは、整数 value 引数を parameter で除算して float の結果を返します。

parameter 引数の型は、データ バインディングが XAML で定義されているかコードで定義されているかに応じて異なる可能性があります。 コード内で BindingConverterParameter プロパティが設定されている場合、通常は、数値型の値に設定されます。

binding.ConverterParameter = 255;

ConverterParameter プロパティは Object 型なので、C# コンパイラはリテラルの 255 を整数として解釈し、プロパティにその値を設定します。

ただし、XAML では通常、ConverterParameter は次のように設定されます。

<Label Text="{Binding Red,
                      Converter={StaticResource doubleToInt},
                      ConverterParameter=255,
                      StringFormat='Red = {0:X2}'}" />

255 は数字のように見えますが、ConverterParameterObject 型なので、XAML パーサーでは 255 を文字列として処理します。 そのため、上記の値コンバーターには、parameterfloatint、または string 型の場合に対処する GetParameter メソッドが別途含まれています。

次の XAML 例では、FloatToIntConverter のインスタンス化をそのリソース ディクショナリで行っています。

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:local="clr-namespace:DataBindingDemos"
             x:Class="DataBindingDemos.RgbColorSelectorPage"
             Title="RGB Color Selector">
    <ContentPage.BindingContext>
        <local:RgbColorViewModel Color="Gray" />
    </ContentPage.BindingContext>
    <ContentPage.Resources>
        <Style TargetType="Slider">
            <Setter Property="VerticalOptions" Value="Center" />
        </Style>

        <Style TargetType="Label">
            <Setter Property="HorizontalTextAlignment" Value="Center" />
        </Style>

        <local:FloatToIntConverter x:Key="floatToInt" />
    </ContentPage.Resources>

    <StackLayout Margin="20">
        <BoxView Color="{Binding Color}"
                 HeightRequest="100"
                 WidthRequest="100"
                 HorizontalOptions="Center" />
        <StackLayout Margin="10, 0">
            <Label Text="{Binding Name}" />
            <Slider Value="{Binding Red}" />
            <Label Text="{Binding Red,
                                  Converter={StaticResource floatToInt},
                                  ConverterParameter=255,
                                  StringFormat='Red = {0:X2}'}" />
            <Slider Value="{Binding Green}" />
            <Label Text="{Binding Green,
                                  Converter={StaticResource floatToInt},
                                  ConverterParameter=255,
                                  StringFormat='Green = {0:X2}'}" />
            <Slider Value="{Binding Blue}" />
            <Label>
                <Label.Text>
                    <Binding Path="Blue"
                             StringFormat="Blue = {0:X2}"
                             Converter="{StaticResource floatToInt}">
                        <Binding.ConverterParameter>
                            <x:Single>255</x:Single>
                        </Binding.ConverterParameter>
                    </Binding>
                </Label.Text>
            </Label>
        </StackLayout>
    </StackLayout>
</ContentPage>

Red および Green プロパティの値は、Binding マークアップ拡張を使って表示されます。 ただし、Blue プロパティは Binding クラスをインスタンス化して、明示的な float 値を ConverterParameter プロパティに設定できる方法を示します。

RGB color selector.