次の方法で共有


Windows データ バインディングの概要

このトピックでは、コントロール (またはその他の UI 要素) を 1 つの項目にバインドする方法、または Windows App SDK アプリ内の項目のコレクションに項目コントロールをバインドする方法について説明します。 さらに、項目のレンダリングを制御する方法、選択内容に基づいて詳細ビューを実装する方法、表示するデータを変換する方法を示します。 詳細については、「データ バインディングの詳細な情報 」を参照してください。

前提 条件

このトピックでは、基本的な Windows App SDK アプリを作成する方法を知っていることを前提としています。 最初の Windows App SDK アプリを作成する手順については、「初めての WinUI 3 (Windows App SDK) プロジェクトを作成する」を参照してください。

プロジェクトを作成する

新しい空のアプリ、パッケージ (デスクトップの WinUI 3) C# プロジェクトを作成します。 "クイック スタート" という名前を付けます。

1 つの項目へのバインド

すべてのバインディングは、バインディング ターゲットとバインディング ソースで構成されます。 通常、ターゲットはコントロールまたはその他の UI 要素のプロパティであり、ソースはクラス インスタンス (データ モデルまたはビュー モデル) のプロパティです。 この例では、コントロールを 1 つの項目にバインドする方法を示します。 ターゲットは、TextBlockText プロパティです。 ソースは、オーディオ録音を表す 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; }
    }
}

最後に、TextBlockViewModel.DefaultRecording.OneLineSummary プロパティにバインドします。

<Window x:Class="Quickstart.MainWindow" ... >
    <Grid>
        <TextBlock Text="{x:Bind ViewModel.DefaultRecording.OneLineSummary}"
    HorizontalAlignment="Center"
    VerticalAlignment="Center"/>
    </Grid>
</Window>

結果を次に示します。

テキストブロックのバインド のバインド

項目のコレクションへのバインド

一般的なシナリオは、ビジネス オブジェクトのコレクションにバインドすることです。 C# では、ジェネリック ObservableCollectionT クラスは、INotifyPropertyChanged と INotifyCollectionChanged インターフェイス 実装するため、データ バインディングに適したコレクションの選択肢です。 これらのインターフェイスは、項目が追加または削除されたとき、またはリスト自体のプロパティが変更されたときに、バインディングに変更通知を提供します。 バインドされたコントロールをコレクション内のオブジェクトのプロパティに変更して更新する場合は、ビジネス オブジェクトも INotifyPropertyChanged実装する必要があります。 詳細については、「データ バインディングの詳細」を参照してください。

次の例では、ListViewRecording オブジェクトのコレクションにバインドします。 まず、ビュー モデルにコレクションを追加します。 これらの新しいメンバーを 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) });
    }
}

次に、ListViewViewModel.Recordings プロパティにバインドします。

<Window x:Class="Quickstart.MainWindow" ... >
    <Grid>
        <ListView ItemsSource="{x:Bind ViewModel.Recordings}"
        HorizontalAlignment="Center" VerticalAlignment="Center"/>
    </Grid>
</Window>

Recording クラスのデータ テンプレートをまだ提供していないので、UI フレームワークで実行できる最善の方法は、ListView内の各項目に対して ToString を呼び出す方法です。 ToString の既定の実装では、型名を返します。

リスト ビューのバインド 1

これを解決するには、ToString をオーバーライドして OneLineSummaryの値を返すか、データ テンプレートを指定します。 データ テンプレート オプションは、より一般的なソリューションであり、柔軟性が高くなります。 データ テンプレートは、コンテンツ コントロールの 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>

リスト ビューのバインド 2

<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>

リスト ビューのバインド 3

XAML 構文の詳細については、「XAMLを使用して UI を作成する」を参照してください。 コントロール レイアウトの詳細については、「XAMLを使用してレイアウトを定義する」を参照してください。

詳細ビューの追加

ListView アイテム内の Recording オブジェクトのすべての詳細を表示することを選択できます。 しかし、それは多くのスペースを占有します。 代わりに、項目を識別するのに十分なデータだけをアイテムに表示し、ユーザーが選択を行うときに、選択した項目のすべての詳細を、詳細ビューと呼ばれる別の UI に表示できます。 この配置は、マスター/詳細ビュー、またはリスト/詳細ビューとも呼ばれます。

これについては 2 つの方法があります。 詳細ビューは、ListViewSelectedItem プロパティにバインドできます。 または、CollectionViewSourceを使用できます。この場合、ListView と詳細ビューの両方を CollectionViewSource にバインドします (これにより、現在選択されている項目が自動的に処理されます)。 両方の手法を次に示します。両方とも同じ結果を得られます (図を参照)。

手記

ここまで、このトピックでは{x:Bind} マークアップ拡張のみを使用してきましたが、以下に示す手法の両方で、{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 手法では、最初に最上位レベルの Gridのリソースとして CollectionViewSource を追加します。

<Grid.Resources>
    <CollectionViewSource x:Name="RecordingsCollection" Source="{x:Bind ViewModel.Recordings}"/>
</Grid.Resources>

手記

WinUI の Window クラスには、Resources プロパティがありません。 代わりに、最上位の Grid (または StackPanelなどの他の親 UI 要素) 要素に CollectionViewSource を追加できます。 Page内で作業している場合は、CollectionViewSourcePage.Resourcesに追加できます。

次に、ListView (名前を付ける必要がなくなったもの) と詳細ビューのバインドを調整して、CollectionViewSourceを使用します。 詳細ビューを CollectionViewSourceに直接バインドすることで、コレクション自体にパスが見つからないバインド内の現在の項目にバインドすることを意味することに注意してください。 バインディングのパスとして CurrentItem プロパティを指定する必要はありませんが、あいまいさがある場合は指定できます。

...
<ListView ItemsSource="{Binding Source={StaticResource RecordingsCollection}}">
...
<StackPanel DataContext="{Binding Source={StaticResource RecordingsCollection}}" ...>
...

各ケースで同じ結果を次に示します。

リスト ビューのバインド 4

表示するデータ値の書式設定または変換

上記のレンダリングに問題があります。 ReleaseDateTime プロパティは単なる日付ではなく、DateTimeです。 そのため、必要以上の精度で表示されています。 1 つの解決策は、ReleaseDateTime.ToString("d")と同等のものを返す文字列プロパティを Recording クラスに追加することです。 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 のバインドで使用できるようになりました。

<Grid.Resources>
    ...
    <local:StringFormatter x:Key="StringFormatterValueConverter"/>
</Grid.Resources>
...
<TextBlock Text="{Binding ReleaseDateTime,
    Converter={StaticResource StringFormatterValueConverter},
    ConverterParameter=Released: \{0:d\}}"/>
...

前述のように、書式設定の柔軟性のために、マークアップを使用して、コンバーター パラメーターを使用してコンバーターに書式文字列を渡します。 このトピックに示すコード例では、C# 値コンバーターはそのパラメーターを使用します。

結果を次に示します。

カスタム書式設定 を使用して日付を表示する