データ バインディングの基礎
.NET マルチプラットフォーム アプリ UI (.NET MAUI) データ バインディングを使用すると、2 つのオブジェクトのプロパティをリンクできるため、一方のオブジェクトを変更すると、もう一方のオブジェクトが変更されます。 これは非常に貴重なツールです。データ バインディングはコード内で完全に定義できますが、XAML にはショートカットがあり便利です。
データ バインディング
データ バインディングでは、ソースとターゲットと呼ばれる 2 つのオブジェクトのプロパティを接続します。 コードでは、2 つの手順が必要です:
- ターゲット オブジェクトの
BindingContext
プロパティはソース オブジェクトに設定する必要があります。 SetBinding
メソッド (多くの場合、Binding
と組み合わせて使用) を呼び出して、そのオブジェクトのプロパティをソース オブジェクトのプロパティにバインドする必要があります。
ターゲット プロパティはバインド可能なプロパティでなくてはなりません。つまり、ターゲット オブジェクトは BindableObject の派生元である必要があります。 Label のプロパティ (Text
など) は、バインド可能なプロパティ TextProperty
に関連付けられます。
XAML では、コードで必要なものと同じ 2 つの手順を実行する必要がありますが、SetBinding
マークアップ拡張機能が Binding
呼び出しと Binding
クラスの代わりに使用される点が異なります。 ただし、XAML でデータ バインディングを定義する場合、ターゲット オブジェクトの BindingContext
を設定する方法は複数あります。 コードビハインド ファイルから設定される場合や、x:Static
または StaticResource
マークアップ拡張をする場合、プロパティ要素タグ BindingContext
の内容として設定される場合があります。
ビューからビューへのバインド
データ バインディングを定義して、同じページ上の 2 つのビューのプロパティをリンクできます。 この場合は、x:Reference
マークアップ拡張を使用してターゲット オブジェクトの BindingContext
を設定します。
次の例には、 Slider と 2 つの Label ビューが含まれています。そのうちの 1 つは Slider 値によって回転され、もう 1 つは Slider 値を表示します。
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="XamlSamples.SliderBindingsPage"
Title="Slider Bindings Page">
<StackLayout>
<Label Text="ROTATION"
BindingContext="{x:Reference slider}"
Rotation="{Binding Path=Value}"
FontAttributes="Bold"
FontSize="18"
HorizontalOptions="Center"
VerticalOptions="Center" />
<Slider x:Name="slider"
Maximum="360"
VerticalOptions="Center" />
<Label BindingContext="{x:Reference slider}"
Text="{Binding Value, StringFormat='The angle is {0:F0} degrees'}"
FontAttributes="Bold"
FontSize="18"
HorizontalOptions="Center"
VerticalOptions="Center" />
</StackLayout>
</ContentPage>
Slider には、x:Reference
マークアップ拡張を使用して 2 つの Label ビューで参照する x:Name
属性が含まれています。 x:Reference
バインディング拡張機能は 、参照される要素の名前に設定する、Name
という名前のプロパティを定義します (この場合は slider
)。 ただし、x:Reference
マークアップ拡張を定義する ReferenceExtension
クラスも Name
の ContentProperty
属性を定義します。つまり、明示的には必要ないということです。
Binding
マークアップ拡張機能自体は、Binding
や BindingBase
クラスのような複数のプロパティを持つことができます。 Binding
の ContentProperty
は Path
ですが、マークアップ拡張の "Path =" 部分は、そのパスが Binding
マークアップ拡張の最初の項目であれば省略できます。
2 番目の Binding
マークアップ拡張は、StringFormat
プロパティを設定します。 .NET MAUI では、バインディングは暗黙的な型変換を実行しません。文字列以外のオブジェクトを文字列として表示する必要がある場合は、型コンバーターを指定するか、StringFormat
を使用しなくてはなりません。
重要
書式設定文字列は、単一引用符で囲む必要があります。
バインディング モード
1 つのビューで、いくつかのプロパティにデータ バインディングを設定できます。 ただし、各ビューには 1 つの BindingContext
しか含められないため、そのビューの複数のデータ バインディングは、同じオブジェクトのあらゆるプロパティを参照する必要があります。
これと他の問題の解決策には、Mode
プロパティが含まれます 。これは BindingMode
列挙型のメンバーに設定されます。
Default
OneWay
— 値はソースからターゲットに転送されますOneWayToSource
— 値はターゲットからソースに転送されますTwoWay
— 値はソースとターゲットの間で両方向に転送されますOneTime
— データはソースからターゲットに移動しますが、BindingContext
が変更された場合のみとなります
次の例は、OneWayToSource
と TwoWay
の一般的な使い方の一つを示しています。
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="XamlSamples.SliderTransformsPage"
Padding="5"
Title="Slider Transforms Page">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<!-- Scaled and rotated Label -->
<Label x:Name="label"
Text="TEXT"
HorizontalOptions="Center"
VerticalOptions="CenterAndExpand" />
<!-- Slider and identifying Label for Scale -->
<Slider x:Name="scaleSlider"
BindingContext="{x:Reference label}"
Grid.Row="1" Grid.Column="0"
Maximum="10"
Value="{Binding Scale, Mode=TwoWay}" />
<Label BindingContext="{x:Reference scaleSlider}"
Text="{Binding Value, StringFormat='Scale = {0:F1}'}"
Grid.Row="1" Grid.Column="1"
VerticalTextAlignment="Center" />
<!-- Slider and identifying Label for Rotation -->
<Slider x:Name="rotationSlider"
BindingContext="{x:Reference label}"
Grid.Row="2" Grid.Column="0"
Maximum="360"
Value="{Binding Rotation, Mode=OneWayToSource}" />
<Label BindingContext="{x:Reference rotationSlider}"
Text="{Binding Value, StringFormat='Rotation = {0:F0}'}"
Grid.Row="2" Grid.Column="1"
VerticalTextAlignment="Center" />
<!-- Slider and identifying Label for RotationX -->
<Slider x:Name="rotationXSlider"
BindingContext="{x:Reference label}"
Grid.Row="3" Grid.Column="0"
Maximum="360"
Value="{Binding RotationX, Mode=OneWayToSource}" />
<Label BindingContext="{x:Reference rotationXSlider}"
Text="{Binding Value, StringFormat='RotationX = {0:F0}'}"
Grid.Row="3" Grid.Column="1"
VerticalTextAlignment="Center" />
<!-- Slider and identifying Label for RotationY -->
<Slider x:Name="rotationYSlider"
BindingContext="{x:Reference label}"
Grid.Row="4" Grid.Column="0"
Maximum="360"
Value="{Binding RotationY, Mode=OneWayToSource}" />
<Label BindingContext="{x:Reference rotationYSlider}"
Text="{Binding Value, StringFormat='RotationY = {0:F0}'}"
Grid.Row="4" Grid.Column="1"
VerticalTextAlignment="Center" />
</Grid>
</ContentPage>
この例では、4 つのSliderビューは Label のRotateY
、Scale
、RotateX
、Rotate
プロパティを制御するよう意図されています。 最初は、 Label の 4 つのプロパティがそれぞれ Slider によって設定されているため、ターゲットのデータをバインドしているように見えます。 ただし、BindingContext
の Label は 1 つのオブジェクトだけであり、4 つの異なるスライダーがあります。 そのため、 4 つの各スライダーの BindingContext
が Label に設定され、バインドがスライダーの Value
プロパティで設定されます。 OneWayToSource
と Value
を使用することで、これらの TwoWay
プロパティはソース プロパティ (RotateY
の Label、Scale
、Rotate
、RotateX
プロパティ) を設定できます。
3 つの Slider ビューのバインドは OneWayToSource
です。つまり、Slider 値によってその label
のプロパティが変わります (Label という名前の BindingContext
)。 これら 3 つの Slider ビューにより、Label のRotateX
、Rotate
、RotateY
プロパティが変更されます。
しかし、 Scale
プロパティのバインディングは TwoWay
です。 これは、Scale
プロパティの既定値が 1 で、Slider バインディングを使用すると、TwoWay
の初期値が 0 ではなく 1 に設定されるためです。 そのバインディングが OneWayToSource
の場合、 Scale
プロパティは最初は Slider 既定値から 0 に設定されます。 Label は表示されません。
注
VisualElement クラスにも ScaleX
と ScaleY
プロパティがあり、それぞれ x 軸と y 軸で VisualElement を拡大縮小します。
バインディングとコレクション
ListView は、IEnumerable
型の ItemsSource
プロパティを定義し、そのコレクションの項目を表示します。 これらの項目は、任意の型のオブジェクトにすることができます。 既定では、 ListView は各項目の ToString
メソッドを使用してその項目を表示します。 これが必要なこともありますが、多くの場合、ToString
はオブジェクトの完全修飾クラス名のみを返します。
ただし、ListView コレクションの項目は、 テンプレート を使用して任意の方法で表示できます。これには、Cell から派生したクラスが含まれます。 テンプレートは、ListView のすべての項目に対して複製され、テンプレートで設定されているデータ バインディングが個々の複製に転送されます。 項目のカスタム セルは ViewCell クラスを使用して作成できます。
ListView は、NamedColor
クラスを使用して、.NET MAUI で使用できるすべての名前付きカラーの一覧を 表示できます。
using System.Reflection;
using System.Text;
namespace XamlSamples
{
public class NamedColor
{
public string Name { get; private set; }
public string FriendlyName { get; private set; }
public Color Color { get; private set; }
// Expose the Color fields as properties
public float Red => Color.Red;
public float Green => Color.Green;
public float Blue => Color.Blue;
public static IEnumerable<NamedColor> All { get; private set; }
static NamedColor()
{
List<NamedColor> all = new List<NamedColor>();
StringBuilder stringBuilder = new StringBuilder();
// Loop through the public static fields of the Color structure.
foreach (FieldInfo fieldInfo in typeof(Colors).GetRuntimeFields())
{
if (fieldInfo.IsPublic &&
fieldInfo.IsStatic &&
fieldInfo.FieldType == typeof(Color))
{
// Convert the name to a friendly name.
string name = fieldInfo.Name;
stringBuilder.Clear();
int index = 0;
foreach (char ch in name)
{
if (index != 0 && Char.IsUpper(ch))
{
stringBuilder.Append(' ');
}
stringBuilder.Append(ch);
index++;
}
// Instantiate a NamedColor object.
NamedColor namedColor = new NamedColor
{
Name = name,
FriendlyName = stringBuilder.ToString(),
Color = (Color)fieldInfo.GetValue(null)
};
// Add it to the collection.
all.Add(namedColor);
}
}
all.TrimExcess();
All = all;
}
}
}
各NamedColor
オブジェクトには、string
型の Name
および FriendlyName
プロパティ、Color 型の Color
プロパティ、および Red
、Green
、Blue
プロパティがあります。 さらに、NamedColor
静的コンストラクターは、Colors クラス内の型Colorのフィールドに対応する NamedColor
オブジェクトを含む IEnumerable<NamedColor>
コレクションを作成し、その公開用静的 All
プロパティに割り当てます。
静的NamedColor.All
プロパティを ListView の ItemsSource
に設定するには、x:Static
マークアップ拡張を使用します。
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:XamlSamples;assembly=XamlSamples"
x:Class="XamlSamples.ListViewDemoPage"
Title="ListView Demo Page">
<ListView ItemsSource="{x:Static local:NamedColor.All}" />
</ContentPage>
その結果、項目が XamlSamples.NamedColor
型になります。
項目のテンプレートを定義するには、ViewCellを参照する DataTemplate に ItemTemplate
を設定する必要があります。 ViewCell は、各項目を表示する 1 つ以上のビューのレイアウトを定義する必要があります。
<ListView ItemsSource="{x:Static local:NamedColor.All}">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Label Text="{Binding FriendlyName}" />
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
注
セルとセルの子のバインディング ソースは、ListView.ItemsSource
コレクションです 。
この例では、Label 要素は ViewCell プロパティの View に設定されています。 ViewCell.View
タグは必要ありません。View プロパティは ViewCell の content プロパティであるためです。 この XAML は、各 NamedColor
オブジェクトの FriendlyName
プロパティを表示します。
項目テンプレートを展開すると、詳細情報と実際の色を表示できます:
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:XamlSamples"
x:Class="XamlSamples.ListViewDemoPage"
Title="ListView Demo Page">
<ContentPage.Resources>
<x:Double x:Key="boxSize">50</x:Double>
<x:Int32 x:Key="rowHeight">60</x:Int32>
<local:FloatToIntConverter x:Key="intConverter" />
</ContentPage.Resources>
<ListView ItemsSource="{x:Static local:NamedColor.All}"
RowHeight="{StaticResource rowHeight}">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout Padding="5, 5, 0, 5"
Orientation="Horizontal"
Spacing="15">
<BoxView WidthRequest="{StaticResource boxSize}"
HeightRequest="{StaticResource boxSize}"
Color="{Binding Color}" />
<StackLayout Padding="5, 0, 0, 0"
VerticalOptions="Center">
<Label Text="{Binding FriendlyName}"
FontAttributes="Bold"
FontSize="14" />
<StackLayout Orientation="Horizontal"
Spacing="0">
<Label Text="{Binding Red,
Converter={StaticResource intConverter},
ConverterParameter=255,
StringFormat='R={0:X2}'}" />
<Label Text="{Binding Green,
Converter={StaticResource intConverter},
ConverterParameter=255,
StringFormat=', G={0:X2}'}" />
<Label Text="{Binding Blue,
Converter={StaticResource intConverter},
ConverterParameter=255,
StringFormat=', B={0:X2}'}" />
</StackLayout>
</StackLayout>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</ContentPage>
バインディングの値コンバーター
前の XAML の例では、それぞれの NamedColor
の Red
、Green
、および Blue
プロパティが表示されています。 これらのプロパティの型は float
で、範囲は 0 ~ 1 です。 16 進数の値を表示する場合は、単に "X2" 書式指定で StringFormat
を使用することはできません。 これは整数に対してのみ機能し、float
値に 255 を乗じる必要があります。
この問題は、値コンバーター(バインディング コンバーター とも呼ばれます) で解決できます。 これは IValueConverter インターフェイスを実装するクラスです。つまり、ConvertBack
と Convert
という名前の 2 つのメソッドがあります。 Convert
メソッドは、値がソースからターゲットに転送されると呼び出されます。 ConvertBack
メソッドは、OneWayToSource
または TwoWay
バインディングでターゲットからソースへの転送に対して呼び出されます。
using System.Globalization;
namespace XamlSamples
{
public class FloatToIntConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
float multiplier;
if (!float.TryParse(parameter as string, out multiplier))
multiplier = 1;
return (int)Math.Round(multiplier * (float)value);
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
float divider;
if (!float.TryParse(parameter as string, out divider))
divider = 1;
return ((float)(int)value) / divider;
}
}
}
注
この例では、バインディングがソースからターゲットへの一方向のみなので、ConvertBack
メソッドは役割を果たしません。
バインディングは、Converter
プロパティを使用してバインディング コンバーターを参照します。 バインディング コンバーターは、ConverterParameter
プロパティで指定されたパラメーターを受け入れることもできます。 ある程度の汎用性のため、これは乗数の指定方法になっています。 バインディング コンバーターは、有効な float
値のコンバーター パラメーターをチェックします。
コンバーターはページのリソース ディクショナリでインスタンス化されるため、複数のバインド間で共有できます。
<local:FloatToIntConverter x:Key="intConverter" />
3 つのデータ バインディングで、この単一インスタンスが参照されます。
<Label Text="{Binding Red,
Converter={StaticResource intConverter},
ConverterParameter=255,
StringFormat='R={0:X2}'}" />
項目テンプレートは、色、フレンドリ名、および RGB 値を表示します。
ListView は、基になるデータで動的に発生する変更を処理できますが、特定の手順を実行する必要があります。 項目のコレクションがランタイム中に ListView 変更の ItemsSource
プロパティに割り当てられた場合は、これらの項目の ObservableCollection<T> クラスを使用します。 ObservableCollection<T> は INotifyCollectionChanged
インターフェイスを実装し、 ListView は CollectionChanged
イベントのハンドラーをインストールします。
ランタイム中に項目自体のプロパティが変更された場合、コレクション内の項目は PropertyChanged
イベントを使用してプロパティ値への INotifyPropertyChanged
インターフェイスと信号の変更を実装する必要があります。
次のステップ
データ バインディングは、ページ内の 2 つのオブジェクト間、またはビジュアル オブジェクトと基になるデータの間でプロパティをリンクする強力なメカニズムを提供します。 しかし、アプリケーションがデータ ソースの操作を開始すると、一般的なアプリ アーキテクチャ パターンが便利なパラダイムとして出現し始めます。
.NET MAUI
フィードバック
https://aka.ms/ContentUserFeedback」を参照してください。
以下は間もなく提供いたします。2024 年を通じて、コンテンツのフィードバック メカニズムとして GitHub の issue を段階的に廃止し、新しいフィードバック システムに置き換えます。 詳細については、「フィードバックの送信と表示