バインディング モード
すべての .NET Multi-Platform App UI (.NET MAUI) のバインド可能プロパティには、既定のバインド モードがあります。既定のバインド モードは、バインド可能なプロパティの作成時に設定し、BindableProperty オブジェクトの DefaultBindingMode
プロパティから利用できます。 この既定のバインディング モードは、バインド可能なプロパティがデータ バインディングのターゲットであるときに有効なモードを示します。 Rotation
、Scale
、Opacity
などのほとんどのプロパティにおいて、既定のバインディング モードは 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 がターゲットです。 このバインディングでは、Label の Opacity
プロパティが参照され、この既定値は 1 になっています。 したがって、Slider の値は、Label の Opacity
の初期値から 1 に初期化されます。 次のスクリーンショットにこれを示します。
さらに、Slider は引き続き動作します。 これは、Slider の Value
プロパティの規定のバインド モードが TwoWay
であるためです。 つまり、Value
プロパティがデータ バインディングのターゲットである場合、ターゲットはソースから設定されますが、ソースもターゲットから設定されます。 これにより、Opacity
の初期値から Slider を設定することができます。
Note
バインド可能プロパティは、プロパティが実際に変更されない限り、プロパティの変更を通知しません。 これにより無限ループは回避されます。
ターゲット プロパティでの既定のバインド モードが特定のデータ バインディングに適していない場合は、Binding
の Mode
プロパティ (または Binding
マークアップ拡張機能の Mode
プロパティ) を BindingMode
列挙型のメンバーの 1 つに設定することで、それをオーバーライドすることができます。
Default
TwoWay
: データは、ソースとターゲット間で両方向に移動しますOneWay
: データは、ソースからターゲットに移動しますOneWayToSource
: データは、ターゲットからソースに移動しますOneTime
: データは、ソースからターゲットに移動しますが、BindingContext
が変更された場合のみです
両方向のバインド
大部分のバインド可能プロパティの既定のバインド モードは OneWay
ですが、次のような一部のプロパティについては、既定のバインド モードは TwoWay
です。
- DatePicker の
Date
プロパティ - Editor、Entry、SearchBar、EntryCell の
Text
プロパティ - ListView の
IsRefreshing
プロパティ MultiPage
のSelectedItem
プロパティ- Picker の
SelectedIndex
プロパティとSelectedItem
プロパティ - Slider と Stepper の
Value
プロパティ - Switch の
IsToggled
プロパティ - SwitchCell の
On
プロパティ - TimePicker の
Time
プロパティ
データ バインディングが Model-View-ViewModel (MVVM) パターンで使用される場合、ビューモデル クラスがデータ バインディング ソースであり、Slider などのビューで構成されるビューがデータ バインディングのターゲットであるため、これらのプロパティは TwoWay
として定義されます。 ページ上の各ビューをビューモデルの対応するプロパティの値で初期化する場合が多いため、MVVM バインドは上記の例に似ていますが、ビューの変更はビューモデルのプロパティにも影響します。
ソースへの一方向のバインド
バインド可能な読み取り専用プロパティの既定のバインディング モードは OneWayToSource
になります。 たとえば、ListView の SelectedItem
プロパティには OneWayToSource
の既定のバインド モードがあります。 その理由は、SelectedItem
プロパティでのバインドによってバインディング ソースが設定されるということにあります。
1 回限りのバインド
バインディング モードが OneTime
であるターゲット プロパティは、バインディング コンテキストが変更された場合にのみ更新されます。 このようなターゲット プロパティでのバインディングの場合、ソース プロパティの変更を監視する必要がないため、バインディング インフラストラクチャは簡略化されます。
いくつかのプロパティには、Entry の IsTextPredictionEnabled
プロパティなど、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
クラスは、Hue
、Saturation
、Luminosity
、Color
、Name
プロパティを定義します。 3 つのカラー コンポーネントのいずれかで値が変更されると、Color
プロパティが再計算され、4 つのプロパティすべてに対して PropertyChanged
イベントが発生します。 Color
プロパティが変更されると、NamedColor
クラス内の静的 GetNearestColorName
メソッドによって、最も近い名前付きの色が取得され、Name
プロパティが設定されます。
ビューモデルがバインディング ソースとして設定されると、バインディング インフラストラクチャによってハンドラーが PropertyChanged
イベントにアタッチされます。 このようにして、プロパティへの変更をバインドに通知し、変更された値を基にターゲット プロパティを設定することができます。 ただし、ターゲット プロパティ (またはターゲット プロパティ上の Binding
定義) に OneTime
の BindingMode
が含まれている場合、バインディング インフラストラクチャから 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
として設定されます。 BoxView、Label、および 3 つの Slider ビューでは、ContentPage からバインディング コンテキストが継承されます。 これらのビューはすべてバインディング ターゲットであり、viewmodel のソース プロパティを参照します。 BoxView の Color
プロパティと Label の Text
プロパティの場合、データ バインディングは OneWay
となります。ビュー内のプロパティはビューモデル内のプロパティから設定されます。 しかし、Slider の Value
プロパティでは、TwoWay
バインド モードが使用されます。 これにより、各 Slider はビューモデルから設定できるようになり、ビューモデルを各 Slider から設定できるようになります。
この例の最初の実行時に、BoxView、Label と 3 つの Slider 要素はすべて、ビューモデルがインスタンス化されたときに設定された初期の Color
プロパティに基づいて、ビューモデルから設定されます。
スライダーの操作に応じて、BoxView と Label が更新されます。
バインディング モードのオーバーライド
ターゲット プロパティのバインド モードはオーバーライドすることができます。そのためには、Binding
の Mode
プロパティ (または 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}" />
この例では、Slider が Scale
プロパティの初期値 (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
バインディング モードでオーバーライドする非常に便利なアプリケーションには、ListView の SelectedItem
プロパティがあります。 既定のバインディング モードは OneWayToSource
です。 ビューモデル内のソース プロパティを参照するように、SelectedItem
プロパティ上にデータ バインディングが設定されると、そのソース プロパティは ListView の選択内容から設定されます。 ただし、状況によっては、ListView がビューモデルから初期化されるようにすることもお勧めします。
重要
既定のバインディング モードはコントロールによって異なる場合があり、バインド可能なプロパティの作成時に設定されます。 BindableProperty オブジェクトの DefaultBindingMode
プロパティで使用可能です。
.NET MAUI