次の方法で共有


バインディング モード

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

すべての .NET Multi-Platform App UI (.NET MAUI) のバインド可能プロパティには、既定のバインド モードがあります。既定のバインド モードは、バインド可能なプロパティの作成時に設定し、BindableProperty オブジェクトの DefaultBindingMode プロパティから利用できます。 この既定のバインディング モードは、バインド可能なプロパティがデータ バインディングのターゲットであるときに有効なモードを示します。 RotationScaleOpacity などのほとんどのプロパティにおいて、既定のバインディング モードは OneWay です。 これらのプロパティがデータ バインディングのターゲットである場合、ターゲット プロパティはソースから設定されます。

次の例は、Slider に定義されているデータ バインディングを示しています。

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="DataBindingDemos.ReverseBindingPage"
             Title="Reverse Binding">
    <StackLayout Padding="10, 0">
        <Label x:Name="label"
               Text="TEXT"
               FontSize="80"
               HorizontalOptions="Center"
               VerticalOptions="Center" />
        <Slider x:Name="slider"
                VerticalOptions="Center"
                Value="{Binding Source={x:Reference label},
                                Path=Opacity}" />
    </StackLayout>
</ContentPage>

この例では、Label がデータ バインディング ソースで、Slider がターゲットです。 このバインディングでは、LabelOpacity プロパティが参照され、この既定値は 1 になっています。 したがって、Slider の値は、LabelOpacity の初期値から 1 に初期化されます。 次のスクリーンショットにこれを示します。

Reverse binding.

さらに、Slider は引き続き動作します。 これは、SliderValue プロパティの規定のバインド モードが TwoWay であるためです。 つまり、Value プロパティがデータ バインディングのターゲットである場合、ターゲットはソースから設定されますが、ソースもターゲットから設定されます。 これにより、Opacity の初期値から Slider を設定することができます。

Note

バインド可能プロパティは、プロパティが実際に変更されない限り、プロパティの変更を通知しません。 これにより無限ループは回避されます。

ターゲット プロパティでの既定のバインド モードが特定のデータ バインディングに適していない場合は、BindingMode プロパティ (または Binding マークアップ拡張機能の Mode プロパティ) を BindingMode 列挙型のメンバーの 1 つに設定することで、それをオーバーライドすることができます。

  • Default
  • TwoWay: データは、ソースとターゲット間で両方向に移動します
  • OneWay: データは、ソースからターゲットに移動します
  • OneWayToSource: データは、ターゲットからソースに移動します
  • OneTime: データは、ソースからターゲットに移動しますが、BindingContext が変更された場合のみです

両方向のバインド

大部分のバインド可能プロパティの既定のバインド モードは OneWay ですが、次のような一部のプロパティについては、既定のバインド モードは TwoWay です。

データ バインディングが Model-View-ViewModel (MVVM) パターンで使用される場合、ビューモデル クラスがデータ バインディング ソースであり、Slider などのビューで構成されるビューがデータ バインディングのターゲットであるため、これらのプロパティは TwoWay として定義されます。 ページ上の各ビューをビューモデルの対応するプロパティの値で初期化する場合が多いため、MVVM バインドは上記の例に似ていますが、ビューの変更はビューモデルのプロパティにも影響します。

ソースへの一方向のバインド

バインド可能な読み取り専用プロパティの既定のバインディング モードは OneWayToSource になります。 たとえば、ListViewSelectedItem プロパティには OneWayToSource の既定のバインド モードがあります。 その理由は、SelectedItem プロパティでのバインドによってバインディング ソースが設定されるということにあります。

1 回限りのバインド

バインディング モードが OneTime であるターゲット プロパティは、バインディング コンテキストが変更された場合にのみ更新されます。 このようなターゲット プロパティでのバインディングの場合、ソース プロパティの変更を監視する必要がないため、バインディング インフラストラクチャは簡略化されます。

いくつかのプロパティには、EntryIsTextPredictionEnabled プロパティなど、OneTime の既定のバインディング モードが含まれています。

ビューモデルとプロパティ変更通知

データバインドでビューモデルを使用する場合、ビューモデルはデータバインディング ソースです。 ビューモデルではバインド可能プロパティは定義されませんが、プロパティの値が変更されたタイミングをバインディング インフラストラクチャに通知するメカニズムが実装されます。 この通知メカニズムは INotifyPropertyChanged インターフェイスであり、PropertyChanged という名前の 1 つのイベントを定義しています。 このインターフェイスを実装するクラスでは通常、そのパブリック プロパティのいずれかの値が変更されたときにイベントが発生します。 プロパティが変更されない場合、イベントを発生させる必要はありません。 INotifyPropertyChanged インターフェイスも BindableObject によって実装されます。PropertyChanged イベントは、バインド可能なプロパティで値が変更されるたびに発生します。

次の例では、データ バインディングにより、色相、彩度、明度用の 3 つの Slider 要素を使用して色を選択できます。

public class HslColorViewModel : INotifyPropertyChanged
{
    Color color;
    string name;
    float hue;
    float saturation;
    float luminosity;

    public event PropertyChangedEventHandler PropertyChanged;

    public float Hue
    {
        get
        {
            return hue;
        }
        set
        {
            if (hue != value)
            {
                Color = Color.FromHsla(value, saturation, luminosity);
            }
        }
    }

    public float Saturation
    {
        get
        {
            return saturation;
        }
        set
        {
            if (saturation != value)
            {
                Color = Color.FromHsla(hue, value, luminosity);
            }
        }        
    }

    public float Luminosity
    {
        get
        {
            return luminosity;
        }
        set
        {
            if (luminosity != value)
            {
                Color = Color.FromHsla(hue, saturation, value);
            }
        }
    }

    public Color Color
    {
        get
        {
            return color;
        }
        set
        {
            if (color != value)
            {
                color = value;
                hue = color.GetHue();
                saturation = color.GetSaturation();
                luminosity = color.GetLuminosity();
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Hue"));
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Saturation"));
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Luminosity"));
                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"));
            }
        }
    }
}

この例では、HslColorViewModel クラスは、HueSaturationLuminosityColorName プロパティを定義します。 3 つのカラー コンポーネントのいずれかで値が変更されると、Color プロパティが再計算され、4 つのプロパティすべてに対して PropertyChanged イベントが発生します。 Color プロパティが変更されると、NamedColor クラス内の静的 GetNearestColorName メソッドによって、最も近い名前付きの色が取得され、Name プロパティが設定されます。

ビューモデルがバインディング ソースとして設定されると、バインディング インフラストラクチャによってハンドラーが PropertyChanged イベントにアタッチされます。 このようにして、プロパティへの変更をバインドに通知し、変更された値を基にターゲット プロパティを設定することができます。 ただし、ターゲット プロパティ (またはターゲット プロパティ上の Binding 定義) に OneTimeBindingMode が含まれている場合、バインディング インフラストラクチャから PropertyChanged イベントにハンドラーがアタッチされる必要はありません。 ターゲット プロパティは、BindingContext が変更されたときにのみ更新され、ソース プロパティ自体が変更されたときは更新されません。

次の XAML では、HslColorViewModel が使用されます。

<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.SimpleColorSelectorPage">
    <ContentPage.BindingContext>
        <local:HslColorViewModel Color="MediumTurquoise" />
    </ContentPage.BindingContext>

    <ContentPage.Resources>
        <Style TargetType="Slider">
            <Setter Property="VerticalOptions" Value="CenterAndExpand" />
        </Style>
    </ContentPage.Resources>

    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="*" />
            <RowDefinition Height="*" />
        </Grid.RowDefinitions>
        <BoxView Color="{Binding Color}"
                 Grid.Row="0" />
        <StackLayout Grid.Row="1"
                     Margin="10, 0">
            <Label Text="{Binding Name}"
                   HorizontalTextAlignment="Center" />
            <Slider Value="{Binding Hue}" />
            <Slider Value="{Binding Saturation}" />
            <Slider Value="{Binding Luminosity}" />
        </StackLayout>
    </Grid>
</ContentPage>

この例では、 HslColorViewModel がインスタンス化され、 Color プロパティが設定され、ページの BindingContext として設定されます。 BoxViewLabel、および 3 つの Slider ビューでは、ContentPage からバインディング コンテキストが継承されます。 これらのビューはすべてバインディング ターゲットであり、viewmodel のソース プロパティを参照します。 BoxViewColor プロパティと LabelText プロパティの場合、データ バインディングは OneWay となります。ビュー内のプロパティはビューモデル内のプロパティから設定されます。 しかし、SliderValue プロパティでは、TwoWay バインド モードが使用されます。 これにより、各 Slider はビューモデルから設定できるようになり、ビューモデルを各 Slider から設定できるようになります。

この例の最初の実行時に、BoxViewLabel と 3 つの Slider 要素はすべて、ビューモデルがインスタンス化されたときに設定された初期の Color プロパティに基づいて、ビューモデルから設定されます。

Simple color selector.

スライダーの操作に応じて、BoxViewLabel が更新されます。

バインディング モードのオーバーライド

ターゲット プロパティのバインド モードはオーバーライドすることができます。そのためには、BindingMode プロパティ (または Binding マークアップ拡張機能の Mode プロパティ) を BindingMode 列挙型のメンバーの 1 つに設定します。

ただし、Mode プロパティを設定しても、期待される結果が生成されるとは限りません。 たとえば、次の例では、Mode プロパティを TwoWay に設定しても期待どおりに機能しません。

<Label Text="TEXT"
       FontSize="40"
       HorizontalOptions="Center"
       VerticalOptions="CenterAndExpand"
       Scale="{Binding Source={x:Reference slider},
                       Path=Value,
                       Mode=TwoWay}" />

この例では、SliderScale プロパティの初期値 (1) に初期化されると期待するかもしれませんが、それは行われません。 TwoWay バインディングの初期化の際は、まず、ソースからターゲットが設定されます。これは、Scale プロパティが Slider の既定値 0 に設定されることを意味します。 Slider 上で TwoWay バインディングが設定されると、Slider は最初にソースから設定されます。

または、バインディング モードを OneWayToSource に設定できます。

<Label Text="TEXT"
       FontSize="40"
       HorizontalOptions="Center"
       VerticalOptions="CenterAndExpand"
       Scale="{Binding Source={x:Reference slider},
                       Path=Value,
                       Mode=OneWayToSource}" />

これで、Slider は 1 (Scale の既定値) に初期化されますが、Slider を操作しても Scale プロパティには影響しません。

Note

VisualElement クラスでは、ScaleX プロパティと ScaleY プロパティも定義しています。これにより、VisualElement を縦方向と横方向に別々にスケールできます。

既定のバインディング モードを TwoWay バインディング モードでオーバーライドする非常に便利なアプリケーションには、ListViewSelectedItem プロパティがあります。 既定のバインディング モードは OneWayToSource です。 ビューモデル内のソース プロパティを参照するように、SelectedItem プロパティ上にデータ バインディングが設定されると、そのソース プロパティは ListView の選択内容から設定されます。 ただし、状況によっては、ListView がビューモデルから初期化されるようにすることもお勧めします。

重要

既定のバインディング モードはコントロールによって異なる場合があり、バインド可能なプロパティの作成時に設定されます。 BindableProperty オブジェクトの DefaultBindingMode プロパティで使用可能です。