Windows データ バインディングの概要
このトピックでは、Windows アプリ SDK アプリで、コントロール (または他の UI 要素) を単一の項目にバインドする方法や、項目コントロールを項目のコレクションにバインドする方法を説明します。 また、項目のレンダリングを制御する方法、選択内容に基づいて詳細ビューを実装する方法、表示するデータを変換する方法も紹介します。 詳しくは、「データ バインディングの詳細」をご覧ください。
前提条件
このトピックでは、基本的な Windows アプリ SDK アプリを作成できることを前提としています。 最初のWindows アプリ SDK アプリを作成する手順については、「WinUI 3 (Windows App SDK) プロジェクトを初めて作成する」を参照してください。
プロジェクトを作成する
新しい空のアプリ、パッケージ (デスクトップの WinUI 3) C# プロジェクトを作成します。 "Quickstart" という名前を付けます。
単一アイテムへのバインド
すべてのバインドは、バインディング ターゲットとバインディング ソースからなります。 通常、ターゲットはコントロールまたは他の UI 要素のプロパティであり、ソースはクラス インスタンス (データ モデルやビュー モデル) のプロパティです。 コントロールを単一の項目にバインドする例を次に示します。 ターゲットは TextBlock
の Text
プロパティです。 ソースは、オーディオの録音を表す Recording
という名前の単純なクラスのインスタンスです。 まずクラスを見てみましょう。
プロジェクトに新しいクラスを追加し、そのクラスに Recording
という名前を付けます。
namespace Quickstart
{
public class Recording
{
public string ArtistName { get; set; }
public string CompositionName { get; set; }
public DateTime ReleaseDateTime { get; set; }
public Recording()
{
ArtistName = "Wolfgang Amadeus Mozart";
CompositionName = "Andante in C for Piano";
ReleaseDateTime = new DateTime(1761, 1, 1);
}
public string OneLineSummary
{
get
{
return $"{CompositionName} by {ArtistName}, released: "
+ ReleaseDateTime.ToString("d");
}
}
}
public class RecordingViewModel
{
private Recording defaultRecording = new Recording();
public Recording DefaultRecording { get { return defaultRecording; } }
}
}
次に、マークアップのウィンドウを表すクラスからバインディング ソース クラスを公開します。 これを行うには、RecordingViewModel
型のプロパティを MainWindow.xaml.cs に追加します。
namespace Quickstart
{
public sealed partial class MainWindow : Window
{
public MainWindow()
{
this.InitializeComponent();
ViewModel = new RecordingViewModel();
}
public RecordingViewModel ViewModel{ get; set; }
}
}
最後に、 TextBlock
を ViewModel.DefaultRecording.OneLineSummary
プロパティにバインドします。
<Window x:Class="Quickstart.MainWindow" ... >
<Grid>
<TextBlock Text="{x:Bind ViewModel.DefaultRecording.OneLineSummary}"
HorizontalAlignment="Center"
VerticalAlignment="Center"/>
</Grid>
</Window>
結果は次のようになります。
アイテムのコレクションへのバインド
一般的なシナリオでは、ビジネス オブジェクトのコレクションにバインドします。 C# では、データ バインディングのコレクションには汎用の ObservableCollection<T> クラスが適しています。このクラスは INotifyPropertyChanged インターフェイスと INotifyCollectionChanged インターフェイスを実装するためです。 これらのインターフェイスは、項目が追加または変更された場合や一覧自体のプロパティが変更された場合に、バインディングに変更を通知します。 コレクション内のオブジェクトのプロパティの変更をバインドされたコントロールに反映する場合は、ビジネス オブジェクトでも INotifyPropertyChanged
を実装します。 詳しくは、「データ バインディングの詳細」をご覧ください。
次の例では、ListView を Recording
オブジェクトのコレクションにバインドしています。 最初に、ビュー モデルにコレクションを追加します。 これらの新しいメンバーを RecordingViewModel
クラスに追加するだけです。
public class RecordingViewModel
{
...
private ObservableCollection<Recording> recordings = new ObservableCollection<Recording>();
public ObservableCollection<Recording> Recordings{ get{ return recordings; } }
public RecordingViewModel()
{
recordings.Add(new Recording(){ ArtistName = "Johann Sebastian Bach",
CompositionName = "Mass in B minor", ReleaseDateTime = new DateTime(1748, 7, 8) });
recordings.Add(new Recording(){ ArtistName = "Ludwig van Beethoven",
CompositionName = "Third Symphony", ReleaseDateTime = new DateTime(1805, 2, 11) });
recordings.Add(new Recording(){ ArtistName = "George Frideric Handel",
CompositionName = "Serse", ReleaseDateTime = new DateTime(1737, 12, 3) });
}
}
次に、 ListView を ViewModel.Recordings
プロパティにバインドします。
<Window x:Class="Quickstart.MainWindow" ... >
<Grid>
<ListView ItemsSource="{x:Bind ViewModel.Recordings}"
HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Grid>
</Window>
まだ Recording
クラスのデータ テンプレートを用意していないため、UI フレームワークで ListView の各項目について ToString を呼び出します。 ToString
の既定の実装は、型名を返すことです。
これを修正するには、OneLineSummary
の値を返すように ToString をオーバーライドするか、データ テンプレートを用意します。 データ テンプレート オプションは、より一般的なソリューションであり、より柔軟です。 データ テンプレートを指定するには、コンテンツ コントロールの ContentTemplate プロパティか、項目コントロールの ItemTemplate プロパティを使用します。 Recording
のデータ テンプレートをデザインするための 2 つの方法と結果の図を以下に示します。
<ListView ItemsSource="{x:Bind ViewModel.Recordings}"
HorizontalAlignment="Center" VerticalAlignment="Center">
<ListView.ItemTemplate>
<DataTemplate x:DataType="local:Recording">
<TextBlock Text="{x:Bind OneLineSummary}"/>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<ListView ItemsSource="{x:Bind ViewModel.Recordings}"
HorizontalAlignment="Center" VerticalAlignment="Center">
<ListView.ItemTemplate>
<DataTemplate x:DataType="local:Recording">
<StackPanel Orientation="Horizontal" Margin="6">
<SymbolIcon Symbol="Audio" Margin="0,0,12,0"/>
<StackPanel>
<TextBlock Text="{x:Bind ArtistName}" FontWeight="Bold"/>
<TextBlock Text="{x:Bind CompositionName}"/>
</StackPanel>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
XAML 構文について詳しくは、「XAML を使用した UI の作成」をご覧ください。 コントロール レイアウトについて詳しくは、「XAML を使用したレイアウトの定義」をご覧ください。
詳細ビューの追加
ListView 項目内の Recording
オブジェクトの詳細をすべて表示することを選択できます。 ただし、多くの領域が占有されます。 代わりに、アイテム内の、アイテムの特定に十分なデータのみを表示できます。その後、ユーザーが選択したときに、選択したアイテムの詳細をすべて、詳細ビューという別個の UI に表示できます。 この配置は、マスター/詳細ビューまたはリスト/詳細ビューとも呼ばれます。
これには 2 つの方法があります。 詳細ビューを、ListView の SelectedItem プロパティにバインドできます。 または CollectionViewSource を使用することもできます。その場合、ListView
と詳細ビューの両方を CollectionViewSource
にバインドします (そうすると、現在選択されている項目が自動的に処理されます)。 両方の手法を以下に示します。図のように、いずれも結果は同じになります。
注意
このトピックでは、これまで {x:Bind} マークアップ拡張のみを使用してきましたが、以下に示す 2 つの手法ではより柔軟な (ただし効率は低下する) {Binding} マークアップ拡張が必要です。
まず、SelectedItem の手法を示します。 C# アプリケーションの場合、必要な唯一の変更はマークアップに対するものです。
<Window x:Class="Quickstart.MainWindow" ... >
<Grid>
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
<ListView x:Name="recordingsListView" ItemsSource="{x:Bind ViewModel.Recordings}">
<ListView.ItemTemplate>
<DataTemplate x:DataType="local:Recording">
<StackPanel Orientation="Horizontal" Margin="6">
<SymbolIcon Symbol="Audio" Margin="0,0,12,0"/>
<StackPanel>
<TextBlock Text="{x:Bind CompositionName}"/>
</StackPanel>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<StackPanel DataContext="{Binding SelectedItem, ElementName=recordingsListView}"
Margin="0,24,0,0">
<TextBlock Text="{Binding ArtistName}"/>
<TextBlock Text="{Binding CompositionName}"/>
<TextBlock Text="{Binding ReleaseDateTime}"/>
</StackPanel>
</StackPanel>
</Grid>
</Window>
CollectionViewSource の方法の場合、まずウィンドウ リソースとして CollectionViewSource
を追加します。
<Window.Resources>
<CollectionViewSource x:Name="RecordingsCollection" Source="{x:Bind ViewModel.Recordings}"/>
</Window.Resources>
次に、ListView (名前を付ける必要はない) と詳細ビューで、CollectionViewSource を使用するようにバインディングを調整します。 詳細ビューを CollectionViewSource
に直接バインドすることによって、コレクション自体ではパスが見つからない、バインディング内の現在の項目にバインドすることを意味します。 バインディングのパスとして CurrentItem
プロパティを指定する必要はありませんが、あいまいさがある場合は指定することもできます。
...
<ListView ItemsSource="{Binding Source={StaticResource RecordingsCollection}}">
...
<StackPanel DataContext="{Binding Source={StaticResource RecordingsCollection}}" ...>
...
次のように、いずれの場合も結果は同じです。
表示のためのデータ値の書式設定と変換
上のレンダリングには問題があります。 ReleaseDateTime
プロパティは単なる日付ではなく、DateTime です。 そのため、必要とする以上の精度で表示されます。 解決策の 1 つは、Recording
クラスに、ReleaseDateTime.ToString("d")
の同等の値を返す文字列プロパティを追加することです。 そのプロパティに ReleaseDate
という名前を付けると、日付と時刻ではなく、日付が返されることを示します。 ReleaseDateAsString
という名前を付けると、さらに文字列が返されることを示します。
より柔軟な解決策は、値コンバーターと呼ばれるものを使用することです。 独自の値コンバーターを作成する方法の例を示します。 以下のコードを Recording.cs ソース コード ファイルに追加します。
public class StringFormatter : Microsoft.UI.Xaml.Data.IValueConverter
{
// This converts the value object to the string to display.
// This will work with most simple types.
public object Convert(object value, Type targetType,
object parameter, string language)
{
// Retrieve the format string and use it to format the value.
string formatString = parameter as string;
if (!string.IsNullOrEmpty(formatString))
{
return string.Format(formatString, value);
}
// If the format string is null or empty, simply
// call ToString() on the value.
return value.ToString();
}
// No need to implement converting back on a one-way binding
public object ConvertBack(object value, Type targetType,
object parameter, string language)
{
throw new NotImplementedException();
}
}
これで、StringFormatter
のインスタンスをページ リソースとして追加し、ReleaseDateTime
プロパティを表示する TextBlock
のバインドで使用することができます。
<Window.Resources>
<local:StringFormatter x:Key="StringFormatterValueConverter"/>
</Window.Resources>
...
<TextBlock Text="{Binding ReleaseDateTime,
Converter={StaticResource StringFormatterValueConverter},
ConverterParameter=Released: \{0:d\}}"/>
...
前述のように、書式設定の柔軟性を確保するために、マークアップを使用し、コンバーター パラメーターを介して書式文字列をコンバーターに渡します。 このトピックに示されているコード例では、C# 値コンバーターがそのパラメーターを使用します。
結果は次のようになります。
関連項目
Windows developer
フィードバック
https://aka.ms/ContentUserFeedback」を参照してください。
以下は間もなく提供いたします。2024 年を通じて、コンテンツのフィードバック メカニズムとして GitHub の issue を段階的に廃止し、新しいフィードバック システムに置き換えます。 詳細については、「フィードバックの送信と表示