次の方法で共有


データ バインディングの詳細

重要な API

このトピックでは、データ バインディング機能について詳しく説明します。 簡単で実用的な概要については、「 データ バインディングの概要」を参照してください。

このトピックでは、 Windows.UI.Xaml.Data 名前空間に存在する API のデータ バインディングについて説明します。

データ バインディングは、アプリの UI でデータを表示し、必要に応じてそのデータと同期し続ける方法です。 データ バインディングを使用すると、データの懸念事項を UI の懸念から切り離すことができます。その結果、概念モデルが単純になり、アプリの読みやすさ、テスト容易性、保守性が向上します。

データ バインディングを使用すると、UI が最初に表示されたときにデータ ソースの値を表示するだけで、それらの値の変更には応答できません。 これは 、1 回限りと呼ばれるバインドのモードであり、実行時に変更されない値に適しています。 または、値を "観察" し、変更されたときに UI を更新することもできます。 このモードは 一方向と呼ばれ、読み取り専用データに適しています。 最終的には、観察と更新の両方を選択して、ユーザーが UI の値に加える変更が自動的にデータ ソースにプッシュバックされるようにすることができます。 このモードは 双方向と呼ばれ、読み取り/書き込みデータに適しています。 いくつかの例を次に示します。

  • 1 回限りのモードを使用して、 イメージ を現在のユーザーの写真にバインドできます。
  • 一方向モードを使用すると、 ListView を新聞セクション別にグループ化されたリアルタイムニュース記事のコレクションにバインドできます。
  • 双方向モードを使用して、フォーム内の顧客の名前に TextBox をバインドできます。

モードに依存せず、2 種類のバインドがあり、どちらも通常 UI マークアップで宣言されます。 {x:Bind} マークアップ拡張または {Binding} マークアップ拡張のいずれかを使用できます。 また、同じ UI 要素でも、同じアプリで 2 つの組み合わせを使用することもできます。 {x:Bind} は Windows 10 の新機能であり、パフォーマンスが向上しています。 特に明記しない限り、このトピックで説明するすべての詳細は、両方の種類のバインディングに適用されます。

{x:Bind} を示すサンプル アプリ

{Binding} を示すサンプル アプリ

  • Bookstore1 アプリをダウンロードします。
  • Bookstore2 アプリをダウンロードします。

すべてのバインディングには、これらの部分が含まれます

  • バインディング ソース。 これはバインディングのデータソースであり、UI に表示する値を持つメンバーを持つ任意のクラスのインスタンスにすることができます。
  • バインディング ターゲット。 これは、データを表示する UI の FrameworkElementDependencyProperty です。
  • バインド オブジェクト。 これは、ソースからターゲットにデータ値を転送し、必要に応じてターゲットからソースに戻す部分です。 バインド オブジェクトは、 {x:Bind} または {Binding} マークアップ拡張から XAML 読み込み時 作成されます。

以降のセクションでは、バインディング ソース、バインド ターゲット、およびバインド オブジェクトについて詳しく説明します。 また、ボタンのコンテンツを、HostViewModel という名前のクラスに属する NextButtonText という名前の文字列プロパティにバインドする例と共にセクションをリンクします。

バインディング ソース

バインディング ソースとして使用できるクラスの基本的な実装を次に示します。

C++/WinRT を使用している場合は、次の C++/WinRT コード例に示すように、新しい Midl File (.idl) 項目をプロジェクトに追加します。 これらの新しいファイルの内容を、一覧に示されている MIDL 3.0 コードに置き換え、プロジェクトをビルドして HostViewModel.h.cppを生成し、生成されたファイルにコードを追加して一覧に一致させます。 生成されたファイルとそのプロジェクトへのコピー方法の詳細については、 XAML コントロールと C++/WinRT プロパティへのバインドに関するページを参照してください。

public class HostViewModel
{
    public HostViewModel()
    {
        this.NextButtonText = "Next";
    }

    public string NextButtonText { get; set; }
}
// HostViewModel.idl
namespace DataBindingInDepth
{
    runtimeclass HostViewModel
    {
        HostViewModel();
        String NextButtonText;
    }
}

// HostViewModel.h
// Implement the constructor like this, and add this field:
...
HostViewModel() : m_nextButtonText{ L"Next" } {}
...
private:
    std::wstring m_nextButtonText;
...

// HostViewModel.cpp
// Implement like this:
...
hstring HostViewModel::NextButtonText()
{
    return hstring{ m_nextButtonText };
}

void HostViewModel::NextButtonText(hstring const& value)
{
    m_nextButtonText = value;
}
...

HostViewModel とそのプロパティ NextButtonText の実装は、1 回限りのバインドにのみ適しています。 ただし、一方向と双方向のバインドは非常に一般的であり、このようなバインドでは、バインディング ソースのデータ値の変更に応じて UI が自動的に更新されます。 このようなバインドが正しく機能するためには、バインディング ソースをバインド オブジェクトに対して "監視可能" にする必要があります。 したがって、この例では、 NextButtonText プロパティに対して一方向または双方向のバインドを行う場合は、実行時にそのプロパティの値に対する変更をバインディング オブジェクトに対して監視可能にする必要があります。

その方法の 1 つは、 DependencyObject からバインディング ソースを表すクラスを派生させ、 DependencyProperty を使用してデータ値を公開することです。 FrameworkElement が監視可能になるのは、その方法です。 FrameworkElements は、すぐに使用できる適切なバインディング ソースです。

クラスを監視可能にするより軽量な方法であり、既に基底クラスを持つクラスに必要な方法は、 System.ComponentModel.INotifyPropertyChanged を実装することです。 これは実際には、 PropertyChanged という名前の 1 つのイベントを実装するだけです。 HostViewModel の使用例を次に示します。

...
using System.ComponentModel;
using System.Runtime.CompilerServices;
...
public class HostViewModel : INotifyPropertyChanged
{
    private string nextButtonText;

    public event PropertyChangedEventHandler PropertyChanged = delegate { };

    public HostViewModel()
    {
        this.NextButtonText = "Next";
    }

    public string NextButtonText
    {
        get { return this.nextButtonText; }
        set
        {
            this.nextButtonText = value;
            this.OnPropertyChanged();
        }
    }

    public void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        // Raise the PropertyChanged event, passing the name of the property whose value has changed.
        this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}
// HostViewModel.idl
namespace DataBindingInDepth
{
    runtimeclass HostViewModel : Windows.UI.Xaml.Data.INotifyPropertyChanged
    {
        HostViewModel();
        String NextButtonText;
    }
}

// HostViewModel.h
// Add this field:
...
    winrt::event_token PropertyChanged(Windows::UI::Xaml::Data::PropertyChangedEventHandler const& handler);
    void PropertyChanged(winrt::event_token const& token) noexcept;

private:
    winrt::event<Windows::UI::Xaml::Data::PropertyChangedEventHandler> m_propertyChanged;
...

// HostViewModel.cpp
// Implement like this:
...
void HostViewModel::NextButtonText(hstring const& value)
{
    if (m_nextButtonText != value)
    {
        m_nextButtonText = value;
        m_propertyChanged(*this, Windows::UI::Xaml::Data::PropertyChangedEventArgs{ L"NextButtonText" });
    }
}

winrt::event_token HostViewModel::PropertyChanged(Windows::UI::Xaml::Data::PropertyChangedEventHandler const& handler)
{
    return m_propertyChanged.add(handler);
}

void HostViewModel::PropertyChanged(winrt::event_token const& token) noexcept
{
    m_propertyChanged.remove(token);
}
...

NextButtonText プロパティが監視可能になりました。 そのプロパティに対して一方向または双方向のバインドを作成すると (後で説明します)、結果のバインド オブジェクトは PropertyChanged イベントをサブスクライブします。 そのイベントが発生すると、バインディング オブジェクトのハンドラーは、変更されたプロパティの名前を含む引数を受け取ります。 このように、バインド オブジェクトは、どのプロパティの値を取得し、再度読み取るかを認識します。

上記のパターンを複数回実装する必要がないように、C# を使用している場合は、QuizGame サンプル ("Common" フォルダー内) にある BindableBase 基本クラスから派生できます。 その外観の例を次に示します。

public class HostViewModel : BindableBase
{
    private string nextButtonText;

    public HostViewModel()
    {
        this.NextButtonText = "Next";
    }

    public string NextButtonText
    {
        get { return this.nextButtonText; }
        set { this.SetProperty(ref this.nextButtonText, value); }
    }
}
// Your BindableBase base class should itself derive from Windows::UI::Xaml::DependencyObject. Then, in HostViewModel.idl, derive from BindableBase instead of implementing INotifyPropertyChanged.

C++/WinRT の場合、基底クラスから派生するアプリケーションで宣言するランタイム クラスは、 コンポーザブル クラスと呼ばれます。 また、コンポーザブル クラスには制約があります。 アプリケーションが Visual Studio と Microsoft Store によって使用される Windows アプリ認定キット テストに合格して申請を検証するには (そのため、アプリケーションが Microsoft Store に正常に取り込まれるには)、最終的に構成可能なクラスが Windows 基本クラスから派生する必要があります。 つまり、継承階層のルートにあるクラスは、Windows.* 名前空間から生成される型である必要があります。 基底クラスからランタイム クラスを派生させる必要がある場合 (たとえば、派生するすべてのビュー モデルに BindableBase クラスを実装する場合)、 Windows.UI.Xaml.DependencyObject から派生させることができます。

引数 String.Empty または null を指定して PropertyChanged イベントを発生させると、オブジェクトのすべての非インデクサー プロパティを再読み取りする必要があることを示します。 イベントを発生させて、オブジェクトのインデクサー プロパティが変更されたことを示すには、特定のインデクサー (インデクサーがインデックス値である場合) に "Item[インデクサー]" の引数を使用するか、すべてのインデ クサー に対して値 "Item[]" を使用します。

バインディング ソースは、プロパティにデータが含まれる単一のオブジェクトとして、またはオブジェクトのコレクションとして扱うことができます。 C# および Visual Basic コードでは、 List(Of T) を実装するオブジェクトに 1 回限りバインドして、実行時に変更されないコレクションを表示できます。 監視可能なコレクション (コレクションへの項目の追加とコレクションからの削除を監視する) の場合は、代わりに ObservableCollection(Of T) に一方向バインドします。 C++/CX コードでは、監視可能なコレクションと監視できないコレクションの両方に対して Vector<T> にバインドでき、C++/WinRT には独自の型があります。 独自のコレクション クラスにバインドするには、次の表のガイダンスを使用します。

Scenario C# と VB (CLR) C++/WinRT C++/CX
オブジェクトにバインドします。 任意のオブジェクトを指定できます。 任意のオブジェクトを指定できます。 オブジェクトは BindableAttribute を持っているか、 ICustomPropertyProvider を実装する必要があります。
バインドされたオブジェクトからプロパティ変更通知を取得します。 オブジェクトは INotifyPropertyChanged を実装する必要があります。 オブジェクトは INotifyPropertyChanged を実装する必要があります。 オブジェクトは INotifyPropertyChanged を実装する必要があります。
コレクションにバインドします。 List(Of T) IInspectable または IBindableObservableVector の IVectorXAML 項目コントロールを参照し、C++/WinRT コレクションと C++/WinRT使用してコレクションにバインドします。 Vector<T>
バインドされたコレクションからコレクション変更通知を取得します。 ObservableCollection(Of T) IInspectable の IObservableVector たとえば、 winrt::single_threaded_observable_vector<T> です。 IObservableVector<T> Vector<T> はこのインターフェイスを実装します。
バインディングをサポートするコレクションを実装します。 List(Of T) を拡張するか、IListIList(Of Object)、IEnumerable、または IEnumerable(Of Object) を実装します。 ジェネリック IList(Of T) および IEnumerable(Of T) へのバインドはサポートされていません。 IInspectableIVector を実装します。 XAML 項目コントロールを参照し、C++/WinRT コレクションと C++/WinRT使用してコレクションにバインドします。 IBindableVectorIBindableIterableIVector<Object^>、IIterable<Object^>、IVector<IInspectable*>、または IIterable<IInspectable*> を実装します。 ジェネリック IVector<T> および IIterable<T> へのバインドはサポートされていません。
コレクション変更通知をサポートするコレクションを実装します。 ObservableCollection(Of T) を拡張するか、(非ジェネリック) IListINotifyCollectionChanged を実装します。 IInspectable または IBindableObservableVector の IObservableVector を実装します IBindableVectorIBindableObservableVector を実装します
増分読み込みをサポートするコレクションを実装します。 ObservableCollection(Of T) を拡張するか、(非ジェネリック) IListINotifyCollectionChanged を実装します。 さらに、 ISupportIncrementalLoading を実装します IInspectable または IBindableObservableVector の IObservableVector を実装します さらに、 ISupportIncrementalLoading を実装します IBindableVectorIBindableObservableVector、および ISupportIncrementalLoading を実装します

増分読み込みを使用して、リスト コントロールを任意の大きなデータ ソースにバインドし、高いパフォーマンスを実現できます。 たとえば、一度にすべての結果を読み込む必要なく、リスト コントロールをBingイメージ クエリ結果にバインドできます。 代わりに、一部の結果のみをすぐに読み込み、必要に応じて追加の結果を読み込みます。 増分読み込みをサポートするには、コレクション変更通知をサポートするデータ ソースに ISupportIncrementalLoading を実装する必要があります。 データ バインディング エンジンがより多くのデータを要求する場合、データ ソースは適切な要求を行い、結果を統合してから、UI を更新するために適切な通知を送信する必要があります。

バインディング ターゲット

次の 2 つの例では、 Button.Content プロパティがバインドターゲットであり、その値はバインディング オブジェクトを宣言するマークアップ拡張に設定されています。 最初 に {x:Bind} が表示され、次に {Binding} が表示されます。 マークアップでバインドを宣言することは一般的なケースです (便利で読みやすく、ツール可能です)。 ただし、マークアップを回避し、必要に応じて、代わりに Binding クラスのインスタンスを強制的に (プログラムで) 作成できます。

<Button Content="{x:Bind ...}" ... />
<Button Content="{Binding ...}" ... />

C++/WinRT または Visual C++ コンポーネント拡張機能 (C++/CX) を使用している場合は、{Binding} マークアップ拡張を使用するランタイム クラスに BindableAttribute 属性を追加する必要があります。

Important

C++/WinRT を使用している場合は、Windows SDK バージョン 10.0.17763.0 (Windows 10 バージョン 1809) 以降をインストールしている場合は、BindableAttribute 属性を使用できます。 この属性がない場合、{Binding} マークアップ拡張を使用できるようにするには、ICustomPropertyProvider インターフェイスと ICustomProperty インターフェイスを実装する必要があります。

{x:Bind} を使用して宣言されたバインド オブジェクト

{x:Bind} マークアップを作成する前に、1 つの手順を実行する必要があります。 マークアップのページを表すクラスからバインディング ソース クラスを公開する必要があります。 これを行うには、MainPage ページ クラスに (この場合は HostViewModel 型の) プロパティを追加します。

namespace DataBindingInDepth
{
    public sealed partial class MainPage : Page
    {
        public MainPage()
        {
            this.InitializeComponent();
            this.ViewModel = new HostViewModel();
        }
    
        public HostViewModel ViewModel { get; set; }
    }
}
// MainPage.idl
import "HostViewModel.idl";

namespace DataBindingInDepth
{
    runtimeclass MainPage : Windows.UI.Xaml.Controls.Page
    {
        MainPage();
        HostViewModel ViewModel{ get; };
    }
}

// MainPage.h
// Include a header, and add this field:
...
#include "HostViewModel.h"
...
    DataBindingInDepth::HostViewModel ViewModel();

private:
    DataBindingInDepth::HostViewModel m_viewModel{ nullptr };
...

// MainPage.cpp
// Implement like this:
...
MainPage::MainPage()
{
    InitializeComponent();

}

DataBindingInDepth::HostViewModel MainPage::ViewModel()
{
    return m_viewModel;
}
...

これで、バインディング オブジェクトを宣言するマークアップを詳しく見ることができます。 次の例では、前の「バインディング ターゲット」セクションで使用したのと同じ Button.Content バインド ターゲットを使用し、 HostViewModel.NextButtonText プロパティにバインドされていることを示しています。

<!-- MainPage.xaml -->
<Page x:Class="DataBindingInDepth.Mainpage" ... >
    <Button Content="{x:Bind Path=ViewModel.NextButtonText, Mode=OneWay}" ... />
</Page>

Path に指定した値に注目 してください。 この値はページ自体のコンテキストで解釈されます。この場合、パスは MainPage ページに追加した ViewModel プロパティを参照することから始まります。 このプロパティは HostViewModel インスタンスを返します。そのため、そのオブジェクトにドットを入れ、 HostViewModel.NextButtonText プロパティにアクセスできます。 また、{x:Bind} の既定値の 1 回限りをオーバーライドするには、Mode を指定します。

Path プロパティは、入れ子になったプロパティ、添付プロパティ、整数および文字列インデクサーにバインドするためのさまざまな構文オプションをサポートしています。 詳細については、「 プロパティ パスの構文」を参照してください。 文字列インデクサーにバインドすると、 ICustomPropertyProvider を実装しなくても、動的プロパティにバインドする効果が得られます。 その他の設定については、 {x:Bind} マークアップ拡張を参照してください。

HostViewModel.NextButtonText プロパティが実際に監視可能であることを示すために、ボタンに Click イベント ハンドラーを追加し、HostViewModel.NextButtonText の値を更新します。 ビルドして実行し、ボタンをクリックすると、ボタンの コンテンツ の更新の値が表示されます。

// MainPage.xaml.cs
private void Button_Click(object sender, RoutedEventArgs e)
{
    this.ViewModel.NextButtonText = "Updated Next button text";
}
// MainPage.cpp
void MainPage::ClickHandler(IInspectable const&, RoutedEventArgs const&)
{
    ViewModel().NextButtonText(L"Updated Next button text");
}

TextBox.Text に対する変更は、TextBox がフォーカスを失ったときに双方向のバインドされたソースに送信されます。すべてのユーザー キーストロークの後に送信されるわけではありません。

DataTemplate と x:DataType

DataTemplate 内 (項目テンプレート、コンテンツ テンプレート、ヘッダー テンプレートのいずれとして使用されるかにかかわらず) では、Path の値はページのコンテキストではなく、テンプレート化されるデータ オブジェクトのコンテキストで解釈されます。 データ テンプレートで {x:Bind} を使用する場合、コンパイル時にバインドを検証 (および効率的なコード生成) できるように、 DataTemplatex:DataType を使用してそのデータ オブジェクトの型を宣言する必要があります。 次の例は、SampleDataGroup オブジェクトのコレクションにバインドされた項目コントロールの ItemTemplate として使用できます。

<DataTemplate x:Key="SimpleItemTemplate" x:DataType="data:SampleDataGroup">
    <StackPanel Orientation="Vertical" Height="50">
      <TextBlock Text="{x:Bind Title}"/>
      <TextBlock Text="{x:Bind Description}"/>
    </StackPanel>
  </DataTemplate>

Path 内の弱く型指定されたオブジェクト

たとえば、Title という名前の文字列プロパティを実装する SampleDataGroup という名前の型があるとします。 また、MainPage.SampleDataGroupAsObject プロパティがあります。これはオブジェクト型ですが、実際には SampleDataGroup のインスタンスを返します。 バインディング <TextBlock Text="{x:Bind SampleDataGroupAsObject.Title}"/> では、型オブジェクトに Title プロパティが見つからないため、コンパイル エラーが発生します。 これを解決するには、次のようにパス構文にキャストを追加します: <TextBlock Text="{x:Bind ((data:SampleDataGroup)SampleDataGroupAsObject).Title}"/>。 Element がオブジェクトとして宣言されているが、実際には TextBlock: <TextBlock Text="{x:Bind Element.Text}"/>である別の例を次に示します。 そして、キャストは問題を解決します: <TextBlock Text="{x:Bind ((TextBlock)Element).Text}"/>

データが非同期的に読み込まれる場合

{x:Bind} をサポートするコードは、コンパイル時にページの部分クラスで生成されます。 これらのファイルは、(C# の場合) objのような名前で、<view name>.g.cs フォルダーにあります。 生成されたコードには、ページの Loading イベントのハンドラーが含まれており、そのハンドラーは、ページのバインドを表す生成されたクラスで Initialize メソッドを呼び出します。 次に初期化 すると 、Update が呼び出され、バインディング ソースとターゲットの間でデータの移動が開始されます。 読み込みは 、ページまたはユーザー コントロールの最初のメジャー パスの直前に発生します。 そのため、データが非同期に読み込まれると、 Initialize が呼び出された時点で準備ができていない可能性があります。 そのため、データを読み込んだ後は、 this.Bindings.Update();を呼び出すことによって、1 回限りのバインドを強制的に初期化できます。 非同期に読み込まれたデータに対して 1 回限りバインドが必要な場合は、一方向のバインドを持ち、変更をリッスンするよりも、このように初期化する方がはるかに安価です。 データがきめ細かい変更を受けず、特定のアクションの一部として更新される可能性が高い場合は、バインドを 1 回限り行い、 Update の呼び出しでいつでも手動更新を強制できます。

{x:Bind} は、JSON オブジェクトのディクショナリ構造の移動やダッキングの入力など、遅延バインディングのシナリオには適していません。 "Duck typing" は、プロパティ名に対する字句の一致に基づく弱い型指定です (たとえば、"歩く、泳ぐ、アヒルのようにクワックする場合、それはアヒルです")。 アヒルのタイピングでは、 Age プロパティへのバインドは Person オブジェクトまたは Wine オブジェクトと同じように満たされます (これらの型に それぞれ Age プロパティがあると仮定します)。 これらのシナリオでは、 {Binding} マークアップ拡張機能を使用します。

{Binding} を使用して宣言されたバインド オブジェクト

C++/WinRT または Visual C++ コンポーネント拡張機能 (C++/CX) を使用している場合、 {Binding} マークアップ拡張を使用するには、バインドするランタイム クラスに BindableAttribute 属性を追加する必要があります。 {x:Bind} を使用するには、その属性は必要ありません。

// HostViewModel.idl
// Add this attribute:
[Windows.UI.Xaml.Data.Bindable]
runtimeclass HostViewModel : Windows.UI.Xaml.Data.INotifyPropertyChanged
...

Important

C++/WinRT を使用している場合は、Windows SDK バージョン 10.0.17763.0 (Windows 10 バージョン 1809) 以降をインストールしている場合は、BindableAttribute 属性を使用できます。 この属性がない場合、{Binding} マークアップ拡張を使用できるようにするには、ICustomPropertyProvider インターフェイスと ICustomProperty インターフェイスを実装する必要があります。

{Binding} では、既定では、マークアップ ページの DataContext にバインドしていることを前提としています。 そこで、ページの DataContext をバインディング ソース クラス (この場合 は HostViewModel 型) のインスタンスに設定します。 次の例は、バインド オブジェクトを宣言するマークアップを示しています。 前の「バインディング ターゲット」セクションで使用したのと同じ Button.Content バインド ターゲットを使用し、 HostViewModel.NextButtonText プロパティにバインドします。

<Page xmlns:viewmodel="using:DataBindingInDepth" ... >
    <Page.DataContext>
        <viewmodel:HostViewModel x:Name="viewModelInDataContext"/>
    </Page.DataContext>
    ...
    <Button Content="{Binding Path=NextButtonText}" ... />
</Page>
// MainPage.xaml.cs
private void Button_Click(object sender, RoutedEventArgs e)
{
    this.viewModelInDataContext.NextButtonText = "Updated Next button text";
}
// MainPage.cpp
void MainPage::ClickHandler(IInspectable const&, RoutedEventArgs const&)
{
    viewModelInDataContext().NextButtonText(L"Updated Next button text");
}

Path に指定した値に注目 してください。 この値は、ページの DataContext のコンテキストで解釈されます。この例では、 HostViewModel のインスタンスに設定されています。 パスは HostViewModel.NextButtonText プロパティを 参照します。 {Binding} の既定値の一方向がここで機能するため、モードを省略できます。

UI 要素の DataContext の既定値は、その親の継承された値です。 もちろん、 DataContext を明示的に設定することで、その既定値をオーバーライドできます。これは、既定では子によって継承されます。 要素に 対して DataContext を明示的に設定すると、同じソースを使用する複数のバインドが必要な場合に便利です。

バインディング オブジェクトには Source プロパティがあります。既定では、バインディングが宣言されている UI 要素の DataContext が使用されます。 この既定値は、バインディングに 対して SourceRelativeSource、または ElementName を 明示的に設定することでオーバーライドできます (詳細については 、{Binding} を参照してください)。

DataTemplate 内では、DataContext はテンプレート化されるデータ オブジェクトに自動的に設定されます。 以下に示す例は、TitleDescription という名前の文字列プロパティを持つ任意の型のコレクションにバインドされた項目コントロールの ItemTemplate として使用できます。

<DataTemplate x:Key="SimpleItemTemplate">
    <StackPanel Orientation="Vertical" Height="50">
      <TextBlock Text="{Binding Title}"/>
      <TextBlock Text="{Binding Description"/>
    </StackPanel>
  </DataTemplate>

既定では、 TextBox が フォーカスを失うと、 TextBox.Text への変更が双方向のバインド されたソースに送信されます。 ユーザーのキー操作のたびに変更が送信されるようにするには、マークアップのバインドで UpdateSourceTriggerPropertyChanged に設定します。 UpdateSourceTrigger明示的に設定することで、変更がソースに送信されるタイミングを完全に制御することもできます。 次に、テキスト ボックス (通常 は TextBox.TextChanged) でイベントを処理し、ターゲットで GetBindingExpression を 呼び出して BindingExpression オブジェクトを取得し、最後に BindingExpression.UpdateSource を 呼び出してデータ ソースをプログラムで更新します。

Path プロパティは、入れ子になったプロパティ、添付プロパティ、整数および文字列インデクサーにバインドするためのさまざまな構文オプションをサポートしています。 詳細については、「 プロパティ パスの構文」を参照してください。 文字列インデクサーにバインドすると、 ICustomPropertyProvider を実装しなくても、動的プロパティにバインドする効果が得られます。 ElementName プロパティは、要素間バインディングに役立ちます。 RelativeSource プロパティにはいくつかの用途があります。そのうちの 1 つは、ControlTemplate 内のテンプレート バインドのより強力な代替手段です。 その他の設定については、 {Binding} マークアップ拡張Binding クラスを参照してください。

ソースとターゲットが同じ型でない場合はどうしますか?

ブール型プロパティの値に基づいて UI 要素の表示を制御する場合、または数値の範囲または傾向の関数である色で UI 要素をレンダリングする場合、または文字列を必要とする UI 要素プロパティに日付または時刻の値を表示する場合は、 その後、ある型から別の型に値を変換する必要があります。 適切なソリューションは、バインディング ソース クラスから適切な型の別のプロパティを公開し、そこで変換ロジックをカプセル化してテスト可能な状態に保つことです。 ただし、ソースプロパティとターゲットプロパティの数が多い場合や、大きな組み合わせがある場合は、柔軟性も拡張性もありません。 その場合は、次の 2 つのオプションがあります。

  • {x:Bind} を使用している場合は、関数に直接バインドして、その変換を行うことができます
  • または、変換を実行するように設計されたオブジェクトである値コンバーターを指定することもできます

値コンバーター

DateTime 値を月を含む文字列値に変換する 1 回限りまたは一方向のバインドに適した値コンバーターを次に示します。 このクラスは IValueConverter を実装します

public class DateToStringConverter : IValueConverter
{
    // Define the Convert method to convert a DateTime value to 
    // a month string.
    public object Convert(object value, Type targetType, 
        object parameter, string language)
    {
        // value is the data from the source object.
        DateTime thisdate = (DateTime)value;
        int monthnum = thisdate.Month;
        string month;
        switch (monthnum)
        {
            case 1:
                month = "January";
                break;
            case 2:
                month = "February";
                break;
            default:
                month = "Month not found";
                break;
        }
        // Return the value to pass to the target.
        return month;
    }

    // ConvertBack is not implemented for a OneWay binding.
    public object ConvertBack(object value, Type targetType, 
        object parameter, string language)
    {
        throw new NotImplementedException();
    }
}
// See the "Formatting or converting data values for display" section in the "Data binding overview" topic.

バインド オブジェクト マークアップでその値コンバーターを使用する方法を次に示します。

<UserControl.Resources>
  <local:DateToStringConverter x:Key="Converter1"/>
</UserControl.Resources>
...
<TextBlock Grid.Column="0" 
  Text="{x:Bind ViewModel.Month, Converter={StaticResource Converter1}}"/>
<TextBlock Grid.Column="0" 
  Text="{Binding Month, Converter={StaticResource Converter1}}"/>

バインディング に対して Converter パラメーターが定義されている場合、バインディング エンジンは Convert メソッドと ConvertBack メソッドを呼び出します。 データがソースから渡されると、バインディング エンジンは Convert を呼び出し、返されたデータをターゲットに渡します。 ターゲットから (双方向バインディングの場合) データが渡されると、バインディング エンジンは ConvertBack を呼び出し、返されたデータをソースに渡します。

コンバーターには、変換で使用する言語を指定できる ConverterLanguage、および変換ロジックのパラメーターを渡すことができる ConverterParameter という省略可能なパラメーターもあります。 コンバーター パラメーターを使用する例については、「 IValueConverter」を参照してください。

変換にエラーがある場合は、例外をスローしないでください。 代わりに、データ転送を停止する DependencyProperty.UnsetValue を返します。

バインディング ソースを解決できないときに使用する既定値を表示するには、マークアップのバインド オブジェクトに FallbackValue プロパティを設定します。 これは、変換と書式設定のエラーを処理するのに役立ちます。 また、異種型のバインドされたコレクション内のすべてのオブジェクトに存在しない可能性があるソース プロパティにバインドする場合にも役立ちます。

文字列以外の値にテキスト コントロールをバインドすると、データ バインディング エンジンによって値が文字列に変換されます。 値が参照型の場合、データ バインディング エンジンは ICustomPropertyProvider.GetStringRepresentation または IStringable.ToString (使用可能な場合) を呼び出して文字列値を取得し、それ以外の場合は Object.ToString を呼び出します。 ただし、バインディング エンジンでは、基底クラスの実装を非表示にする ToString 実装は無視されることに注意してください。 サブクラスの実装では、代わりに基底クラス の ToString メソッドをオーバーライドする必要があります。 同様に、ネイティブ言語では、すべてのマネージド オブジェクトが ICustomPropertyProviderIStringable を実装しているように見えます。 ただし、 GetStringRepresentationIStringable.ToString のすべての呼び出しは 、Object.ToString またはそのメソッドのオーバーライドにルーティングされ、基底クラスの実装を非表示にする新しい ToString 実装にはルーティングされません。

Windows 10 バージョン 1607 以降、XAML フレームワークにはブール型から可視性へのコンバーターが組み込まれています。 コンバーターは trueVisible 列挙値に、 false をCollapsed にマップするため、コンバーターを作成せずに Visibility プロパティをブール値にバインドできます。 組み込みのコンバーターを使用するには、アプリの最小ターゲット SDK バージョンが 14393 以降である必要があります。 アプリが以前のバージョンの Windows 10 を対象とする場合は使用できません。 ターゲット バージョンの詳細については、「 バージョン アダプティブ コード」を参照してください。

{x:Bind} での関数バインド

{x:Bind} を使用すると、バインド パスの最後のステップを関数にすることができます。 これは、変換を実行したり、複数のプロパティに依存するバインドを実行したりするために使用できます。 x:Bind の関数を参照してください

要素間バインド

1 つの XAML 要素のプロパティを別の XAML 要素のプロパティにバインドできます。 マークアップでの外観の例を次に示します。

<TextBox x:Name="myTextBox" />
<TextBlock Text="{x:Bind myTextBox.Text, Mode=OneWay}" />

Important

C++/WinRT を使用した要素間バインディングに必要なワークフローについては、「 要素間バインディング」を参照してください。

{x:Bind} を使用したリソース ディクショナリ

{x:Bind} マークアップ拡張はコード生成に依存するため、InitializeComponent を呼び出すコンストラクターを含む分離コード ファイルが必要です (生成されたコードを初期化するため)。 リソース ディクショナリを再利用する場合は、そのファイル名を参照するのではなく、その型をインスタンス化します ( InitializeComponent が呼び出されるようにします)。 既存のリソース ディクショナリがあり、その中に {x:Bind} を使用する場合の方法の例を次に示します。

TemplatesResourceDictionary.xaml

<ResourceDictionary
    x:Class="ExampleNamespace.TemplatesResourceDictionary"
    .....
    xmlns:examplenamespace="using:ExampleNamespace">
    
    <DataTemplate x:Key="EmployeeTemplate" x:DataType="examplenamespace:IEmployee">
        <Grid>
            <TextBlock Text="{x:Bind Name}"/>
        </Grid>
    </DataTemplate>
</ResourceDictionary>

TemplatesResourceDictionary.xaml.cs

using Windows.UI.Xaml.Data;
 
namespace ExampleNamespace
{
    public partial class TemplatesResourceDictionary
    {
        public TemplatesResourceDictionary()
        {
            InitializeComponent();
        }
    }
}

MainPage.xaml

<Page x:Class="ExampleNamespace.MainPage"
    ....
    xmlns:examplenamespace="using:ExampleNamespace">

    <Page.Resources>
        <ResourceDictionary>
            .... 
            <ResourceDictionary.MergedDictionaries>
                <examplenamespace:TemplatesResourceDictionary/>
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </Page.Resources>
</Page>

再利用可能なスタイルでの {x:Bind} と {Binding} の混在

前の例では DataTemplates で {x:Bind} を使用して示していますが、{x:Bind} マークアップ拡張と {Binding} マークアップ拡張の両方を組み合わせた再利用可能なスタイルを作成することもできます。 これは、{x:Bind} を使用して一部のプロパティをコンパイル時の既知の値にバインドし、他のプロパティを {Binding} を使用してランタイム DataContext 値にバインドする場合に便利です。

両方のバインド 方法を使用する再利用可能な Button スタイルを作成する方法を示す例を次に示します。

TemplatesResourceDictionary.xaml

<ResourceDictionary
    x:Class="ExampleNamespace.TemplatesResourceDictionary"
    .....
    xmlns:examplenamespace="using:ExampleNamespace">
    
    <!-- DataTemplate using x:Bind -->
    <DataTemplate x:Key="EmployeeTemplate" x:DataType="examplenamespace:IEmployee">
        <Grid>
            <TextBlock Text="{x:Bind Name}"/>
        </Grid>
    </DataTemplate>
    
    <!-- Style that mixes x:Bind and Binding -->
    <Style x:Key="CustomButtonStyle" TargetType="Button">
        <Setter Property="Background" Value="{Binding ButtonBackgroundBrush}"/>
        <Setter Property="Foreground" Value="{Binding ButtonForegroundBrush}"/>
        <Setter Property="FontSize" Value="16"/>
        <Setter Property="Margin" Value="4"/>
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="Button">
                    <Border x:Name="RootBorder"
                            Background="{TemplateBinding Background}"
                            BorderBrush="{TemplateBinding BorderBrush}"
                            BorderThickness="{TemplateBinding BorderThickness}"
                            CornerRadius="4">
                        <StackPanel Orientation="Horizontal" 
                                    HorizontalAlignment="Center"
                                    VerticalAlignment="Center">
                            <!-- x:Bind to a static property or page-level property -->
                            <Ellipse Width="8" Height="8" 
                                     Fill="{x:Bind DefaultIndicatorBrush}" 
                                     Margin="0,0,8,0"/>
                            <!-- Binding to DataContext -->
                            <ContentPresenter x:Name="ContentPresenter"
                                              Content="{TemplateBinding Content}"
                                              Foreground="{TemplateBinding Foreground}"
                                              FontSize="{TemplateBinding FontSize}"/>
                        </StackPanel>
                        <VisualStateManager.VisualStateGroups>
                            <VisualStateGroup x:Name="CommonStates">
                                <VisualState x:Name="Normal"/>
                                <VisualState x:Name="PointerOver">
                                    <VisualState.Setters>
                                        <!-- Binding to DataContext for hover color -->
                                        <Setter Target="RootBorder.Background" 
                                                Value="{Binding ButtonHoverBrush}"/>
                                    </VisualState.Setters>
                                </VisualState>
                                <VisualState x:Name="Pressed">
                                    <VisualState.Setters>
                                        <!-- x:Bind to a compile-time known resource -->
                                        <Setter Target="RootBorder.Background" 
                                                Value="{x:Bind DefaultPressedBrush}"/>
                                    </VisualState.Setters>
                                </VisualState>
                            </VisualStateGroup>
                        </VisualStateManager.VisualStateGroups>
                    </Border>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</ResourceDictionary>

TemplatesResourceDictionary.xaml.cs

using Windows.UI;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Media;
 
namespace ExampleNamespace
{
    public partial class TemplatesResourceDictionary
    {
        public TemplatesResourceDictionary()
        {
            InitializeComponent();
        }
        
        // Properties for x:Bind - these are compile-time bound
        public SolidColorBrush DefaultIndicatorBrush { get; } = 
            new SolidColorBrush(Colors.Green);
            
        public SolidColorBrush DefaultPressedBrush { get; } = 
            new SolidColorBrush(Colors.DarkGray);
    }
}

ランタイム値を提供する ViewModel を使用した MainPage.xaml での使用法:

<Page x:Class="ExampleNamespace.MainPage"
    ....
    xmlns:examplenamespace="using:ExampleNamespace">

    <Page.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <examplenamespace:TemplatesResourceDictionary/>
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </Page.Resources>
    
    <Page.DataContext>
        <examplenamespace:ButtonThemeViewModel/>
    </Page.DataContext>

    <StackPanel Margin="20">
        <!-- This button uses the mixed binding style -->
        <Button Content="Save" Style="{StaticResource CustomButtonStyle}"/>
        <Button Content="Cancel" Style="{StaticResource CustomButtonStyle}"/>
    </StackPanel>
</Page>

ButtonThemeViewModel.cs (ランタイム バインド値を提供する DataContext):

using System.ComponentModel;
using Windows.UI;
using Windows.UI.Xaml.Media;

namespace ExampleNamespace
{
    public class ButtonThemeViewModel : INotifyPropertyChanged
    {
        private SolidColorBrush _buttonBackgroundBrush = new SolidColorBrush(Colors.LightBlue);
        private SolidColorBrush _buttonForegroundBrush = new SolidColorBrush(Colors.DarkBlue);
        private SolidColorBrush _buttonHoverBrush = new SolidColorBrush(Colors.LightCyan);

        public SolidColorBrush ButtonBackgroundBrush
        {
            get => _buttonBackgroundBrush;
            set
            {
                _buttonBackgroundBrush = value;
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(ButtonBackgroundBrush)));
            }
        }

        public SolidColorBrush ButtonForegroundBrush
        {
            get => _buttonForegroundBrush;
            set
            {
                _buttonForegroundBrush = value;
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(ButtonForegroundBrush)));
            }
        }

        public SolidColorBrush ButtonHoverBrush
        {
            get => _buttonHoverBrush;
            set
            {
                _buttonHoverBrush = value;
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(ButtonHoverBrush)));
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;
    }
}

この例では:

  • {Binding} は、DataContext (ButtonBackgroundBrush、ButtonForegroundBrush、ButtonHoverBrush) に依存するプロパティに使用されます。
  • {x:Bind} は、コンパイル時に既知であり、ResourceDictionary 自体 (DefaultIndicatorBrush、DefaultPressedBrush) に属するプロパティに使用されます。
  • スタイルは再利用可能であり、任意のボタンに適用できます
  • 静的要素に対する {x:Bind} のパフォーマンスの利点を引き続き活用しながら、DataContext を使用してランタイムテーマを設定できます

イベント バインディングと ICommand

{x:Bind} では 、イベント バインドと呼ばれる機能がサポートされています。 この機能を使用すると、バインディングを使用してイベントのハンドラーを指定できます。これは、分離コード ファイル上のメソッドを使用してイベントを処理する上で追加のオプションです。 MainPage クラスに RootFrame プロパティがあるとします。

public sealed partial class MainPage : Page
{
    ...
    public Frame RootFrame { get { return Window.Current.Content as Frame; } }
}

次に、ボタンの Click イベントを、RootFrame プロパティによって返される Frame オブジェクトのメソッドにバインドできます。 また、ボタンの IsEnabled プロパティを同じ Frame の別のメンバーにバインドします。

<AppBarButton Icon="Forward" IsCompact="True"
IsEnabled="{x:Bind RootFrame.CanGoForward, Mode=OneWay}"
Click="{x:Bind RootFrame.GoForward}"/>

オーバーロードされたメソッドを使用して、この手法でイベントを処理することはできません。 また、イベントを処理するメソッドにパラメーターがある場合は、イベントのすべてのパラメーターの型からすべて割り当て可能である必要があります。 この場合、 Frame.GoForward はオーバーロードされず、パラメーターがありません (ただし、2 つの オブジェクト パラメーターを受け取った場合でも有効です)。 ただし、Frame.GoBack はオーバーロードされているため、この手法ではそのメソッドを使用できません。

イベント バインディング手法は、コマンドの実装と使用に似ています (コマンドは 、ICommand インターフェイスを実装するオブジェクトを返すプロパティです)。 {x:Bind}{Binding} はどちらもコマンドで動作します。 コマンド パターンを複数回実装する必要がないように、QuizGame サンプル ("Common" フォルダー) にある DelegateCommand ヘルパー クラスを使用できます。

フォルダーまたはファイルのコレクションへのバインド

Windows.Storage 名前空間の API を使用して、フォルダーとファイルのデータを取得できます。 ただし、さまざまな GetFilesAsyncGetFoldersAsyncおよび GetItemsAsync メソッドは、リスト コントロールへのバインドに適した値を返しません。 代わりに、FileInformationFactory クラスの GetVirtualizedFilesVectorGetVirtualizedFoldersVectorおよび GetVirtualizedItemsVector メソッドの戻り値にバインドする必要があります。 StorageDataSource と GetVirtualizedFilesVector サンプルの次のコード例は、一般的な使用パターンを示しています。 アプリ パッケージ マニフェストで picturesLibrary 機能を宣言し、Pictures ライブラリ フォルダーに画像があることを確認してください。

protected override void OnNavigatedTo(NavigationEventArgs e)
{
    var library = Windows.Storage.KnownFolders.PicturesLibrary;
    var queryOptions = new Windows.Storage.Search.QueryOptions();
    queryOptions.FolderDepth = Windows.Storage.Search.FolderDepth.Deep;
    queryOptions.IndexerOption = Windows.Storage.Search.IndexerOption.UseIndexerWhenAvailable;

    var fileQuery = library.CreateFileQueryWithOptions(queryOptions);

    var fif = new Windows.Storage.BulkAccess.FileInformationFactory(
        fileQuery,
        Windows.Storage.FileProperties.ThumbnailMode.PicturesView,
        190,
        Windows.Storage.FileProperties.ThumbnailOptions.UseCurrentScale,
        false
        );

    var dataSource = fif.GetVirtualizedFilesVector();
    this.PicturesListView.ItemsSource = dataSource;
}

通常、この方法を使用して、ファイルとフォルダーの情報の読み取り専用ビューを作成します。 たとえば、ユーザーが音楽ビューで曲を評価できるように、ファイルとフォルダーのプロパティへの双方向バインディングを作成できます。 ただし、適切な SavePropertiesAsync メソッド (MusicProperties.SavePropertiesAsync など) を呼び出すまで、変更は保持されません。 項目がフォーカスを失うと、選択のリセットがトリガーされるため、変更をコミットする必要があります。

この手法を使用した双方向バインディングは、Music などのインデックス付きの場所でのみ機能します。 FolderInformation.GetIndexedStateAsync メソッドを呼び出して、場所にインデックスを付けるかどうかを確認できます。

また、仮想化されたベクターは、値を設定する前に、一部の項目に 対して null を 返すことができます。 たとえば、仮想化されたベクターにバインドされたリスト コントロールの SelectedItem 値を使用する前に null を確認するか、代わりに SelectedIndex を使用する必要があります。

キーでグループ化されたデータへのバインド

アイテムのフラット コレクション ( BookSku クラスで表される書籍など) を取得し、共通プロパティをキー ( BookSku.AuthorName プロパティなど) として使用してアイテムをグループ化した場合、結果はグループ化されたデータと呼ばれます。 データをグループ化すると、フラット コレクションではなくなります。 グループ化されたデータはグループ オブジェクトのコレクションであり、各グループ オブジェクトには

  • キー、および
  • そのキーに一致するプロパティを持つ項目のコレクション。

書籍の例をもう一度見ると、著者名で書籍をグループ化した結果、各グループに含まれる著者名グループのコレクションが作成されます。

  • キー(作成者名)、および
  • AuthorName プロパティがグループのキーと一致する BookSkus のコレクション。

一般に、コレクションを表示するには、項目コントロール (ListViewGridView など) の ItemsSource をコレクションを返すプロパティに直接バインドします。 これが項目のフラット コレクションである場合は、特別な操作を行う必要はありません。 ただし、グループ オブジェクトのコレクション (グループ化されたデータにバインドする場合と同様) の場合は、項目コントロールとバインド ソースの間に位置する CollectionViewSource と呼ばれる中間オブジェクトのサービスが必要です。 CollectionViewSource は、グループ化されたデータを返すプロパティにバインドし、項目コントロールを CollectionViewSource にバインドします。 CollectionViewSource の追加の付加価値は、現在の項目を追跡するため、複数の項目を同じ CollectionViewSource にバインドすることで、複数の項目を同期状態に保つことができます。 CollectionViewSource.View プロパティによって返されるオブジェクトの ICollectionView.CurrentItem プロパティを使用して、現在の項目にプログラムでアクセスすることもできます。

CollectionViewSource のグループ化機能をアクティブにするには、IsSourceGrouped を true に設定します ItemsPath プロパティも設定する必要があるかどうかは、グループ オブジェクトの作成方法によって異なります。 グループ オブジェクトを作成するには、"is-a-group" パターンと "has-a-group" パターンの 2 つの方法があります。 "is-a-group" パターンでは、グループ オブジェクトはコレクション型 ( List<T> など) から派生するため、グループ オブジェクトは実際にはそれ自体が項目のグループになります。 このパターンでは、 ItemsPath を設定する必要はありません。 "has-a-group" パターンでは、グループ オブジェクトにはコレクション型の 1 つ以上のプロパティ ( List<T> など) があるため、グループには、プロパティの形式 (または複数のプロパティの形式の複数の項目グループ) の項目のグループが "ある" ようにします。 このパターンでは、 ItemsPath に項目のグループを含むプロパティの名前を設定する必要があります。

次の例は、"has-a-group" パターンを示しています。 ページ クラスには ViewModel という名前のプロパティがあり、ビュー モデルのインスタンスを返します。 CollectionViewSource は、ビュー モデルの Authors プロパティ (Authors はグループ オブジェクトのコレクション) にバインドされ、グループ化された項目を含む Author.BookSkus プロパティであることを指定します。 最後に、 GridViewCollectionViewSource にバインドされ、グループ内の項目をレンダリングできるように、そのグループ スタイルが定義されています。

<Page.Resources>
    <CollectionViewSource
    x:Name="AuthorHasACollectionOfBookSku"
    Source="{x:Bind ViewModel.Authors}"
    IsSourceGrouped="true"
    ItemsPath="BookSkus"/>
</Page.Resources>
...
<GridView
ItemsSource="{x:Bind AuthorHasACollectionOfBookSku}" ...>
    <GridView.GroupStyle>
        <GroupStyle
            HeaderTemplate="{StaticResource AuthorGroupHeaderTemplateWide}" ... />
    </GridView.GroupStyle>
</GridView>

"is-a-group" パターンは、2 つの方法のいずれかで実装できます。 1 つの方法は、独自のグループ クラスを作成することです。 List<T> (T は項目の型) からクラスを派生させます。 たとえば、「 public class Author : List<BookSku> 」のように入力します。 2 つ目の方法は、 LINQ 式を使用して 、BookSku 項目のプロパティ値と同様にグループ オブジェクト (およびグループ クラス) を動的に作成することです。 このアプローチは、項目のフラットなリストのみを保持し、その場でグループ化する方法は、クラウド サービスからデータにアクセスするアプリの一般的な方法です。 作成者やジャンルなどの特別なグループ クラスを必要とせずに、書籍を作成者またはジャンル別にグループ化する柔軟性が得られます。

次の例は、 LINQ を使用した "is-a-group" パターンを示しています。 今回は、ジャンル別に書籍をグループ化し、グループ ヘッダーにジャンル名と共に表示します。 これは 、グループ Key 値を参照する "Key" プロパティ パスによって示されます。

using System.Linq;
...
private IOrderedEnumerable<IGrouping<string, BookSku>> genres;

public IOrderedEnumerable<IGrouping<string, BookSku>> Genres
{
    get
    {
        if (this.genres == null)
        {
            this.genres = from book in this.bookSkus
                          group book by book.genre into grp
                          orderby grp.Key
                          select grp;
        }
        return this.genres;
    }
}

データ テンプレートで {x:Bind} を 使用する場合は、 x:DataType 値を設定してバインドされる型を示す必要があります。 型がジェネリックの場合は、マークアップで表現できないため、代わりにグループ スタイル ヘッダー テンプレートで {Binding} を使用する必要があります。

    <Grid.Resources>
        <CollectionViewSource x:Name="GenreIsACollectionOfBookSku"
        Source="{x:Bind Genres}"
        IsSourceGrouped="true"/>
    </Grid.Resources>
    <GridView ItemsSource="{x:Bind GenreIsACollectionOfBookSku}">
        <GridView.ItemTemplate x:DataType="local:BookTemplate">
            <DataTemplate>
                <TextBlock Text="{x:Bind Title}"/>
            </DataTemplate>
        </GridView.ItemTemplate>
        <GridView.GroupStyle>
            <GroupStyle>
                <GroupStyle.HeaderTemplate>
                    <DataTemplate>
                        <TextBlock Text="{Binding Key}"/>
                    </DataTemplate>
                </GroupStyle.HeaderTemplate>
            </GroupStyle>
        </GridView.GroupStyle>
    </GridView>

SemanticZoom コントロールは、ユーザーがグループ化されたデータを表示および移動するための優れた方法です。 Bookstore2 サンプル アプリは、SemanticZoom の使用方法を示しています。 そのアプリでは、作成者別にグループ化された書籍の一覧 (拡大表示ビュー) を表示したり、縮小して作成者のジャンプ リスト (縮小表示) を表示したりできます。 ジャンプ リストを使用すると、書籍の一覧をスクロールするよりもはるかに高速なナビゲーションが可能になります。 ズームインビューとズームアウトビューは、実際には同じ CollectionViewSource にバインドされた ListView コントロールまたは GridView コントロールです

SemanticZoom の図

カテゴリ内のサブカテゴリなどの階層データにバインドする場合は、一連の項目コントロールを使用して UI に階層レベルを表示することを選択できます。 1 つの項目コントロールで選択すると、後続の項目コントロールの内容が決まります。 各リストを独自の CollectionViewSource にバインドし、CollectionViewSource インスタンスをチェーン内でまとめてバインドすることで、リストの同期を維持できます。 これは、マスター/詳細 (またはリスト/詳細) ビューと呼ばれます。 詳細については、「 階層データにバインドし、マスター/詳細ビューを作成する方法」を参照してください。

データ バインディングの問題の診断とデバッグ

バインディング マークアップには、プロパティの名前が含まれています (また、C# の場合は、フィールドやメソッドの場合もあります)。 そのため、プロパティの名前を変更する場合は、それを参照するバインディングも変更する必要があります。 これを忘れることは、データ バインディングのバグの一般的な例になり、アプリがコンパイルされないか、正しく実行されません。

{x:Bind}{Binding} によって作成されるバインド オブジェクトは、ほぼ機能的に同等です。 ただし、{x:Bind} にはバインディング ソースの型情報があり、コンパイル時にソース コードが生成されます。 {x:Bind} を使用すると、コードの残りの部分で取得するのと同じ種類の問題検出が得られます。 これには、バインド式のコンパイル時検証と、ページの部分クラスとして生成されたソース コードにブレークポイントを設定したデバッグが含まれます。 これらのクラスは、 obj フォルダー内のファイル内にあり、(C#の場合) <view name>.g.csなどの名前が付いています。 バインドに問題がある場合は、Microsoft Visual Studio デバッガーで [ハンドルされない例外の中断 ] をオンにします。 デバッガーはその時点で実行を中断し、問題が発生した内容をデバッグできます。 {x:Bind} によって生成されるコードは、バインディング ソース ノードのグラフの各部分で同じパターンに従います。 また、[呼び出し履歴] ウィンドウの情報を使用して、問題の原因となった呼び出しのシーケンスを特定できます。

{Binding} には、バインディング ソースの型情報がありません。 ただし、デバッガーをアタッチしてアプリを実行すると、Visual Studio の [出力] ウィンドウにバインド エラーが表示されます。

コードでのバインドの作成

手記コードで {x:Bind}バインドを作成できないため、このセクションは {Binding} にのみ適用されます。 ただし、依存関係プロパティの変更通知に登録できる DependencyObject.RegisterPropertyChangedCallback では、{x:Bind} の同じ利点の一部を実現できます。

また、XAML ではなく手続き型コードを使用して、UI 要素をデータに接続することもできます。 これを行うには、新しい Binding オブジェクトを作成し、適切なプロパティを設定してから、 FrameworkElement.SetBinding または BindingOperations.SetBinding を呼び出します。 バインドをプログラムで作成すると、実行時にバインド プロパティの値を選択したり、複数のコントロール間で 1 つのバインドを共有したりする場合に便利です。 ただし、 SetBinding を呼び出した後にバインディング プロパティの値を変更することはできません。

次の例は、コードにバインドを実装する方法を示しています。

<TextBox x:Name="MyTextBox" Text="Text"/>
// Create an instance of the MyColors class 
// that implements INotifyPropertyChanged.
MyColors textcolor = new MyColors();

// Brush1 is set to be a SolidColorBrush with the value Red.
textcolor.Brush1 = new SolidColorBrush(Colors.Red);

// Set the DataContext of the TextBox MyTextBox.
MyTextBox.DataContext = textcolor;

// Create the binding and associate it with the text box.
Binding binding = new Binding() { Path = new PropertyPath("Brush1") };
MyTextBox.SetBinding(TextBox.ForegroundProperty, binding);
' Create an instance of the MyColors class 
' that implements INotifyPropertyChanged. 
Dim textcolor As New MyColors()

' Brush1 is set to be a SolidColorBrush with the value Red. 
textcolor.Brush1 = New SolidColorBrush(Colors.Red)

' Set the DataContext of the TextBox MyTextBox. 
MyTextBox.DataContext = textcolor

' Create the binding and associate it with the text box.
Dim binding As New Binding() With {.Path = New PropertyPath("Brush1")}
MyTextBox.SetBinding(TextBox.ForegroundProperty, binding)

{x:Bind} と {Binding} 機能の比較

特徴 {x:Bind} と {Binding} 注記
Path は既定のプロパティです {x:Bind a.b.c}
-
{Binding a.b.c}
Path プロパティ {x:Bind Path=a.b.c}
-
{Binding Path=a.b.c}
x:Bind では、Path は DataContext ではなく、既定でページにルート化されます。
Indexer {x:Bind Groups[2].Title}
-
{Binding Groups[2].Title}
コレクション内の指定した項目にバインドします。 整数ベースのインデックスのみがサポートされています。
添付プロパティ {x:Bind Button22.(Grid.Row)}
-
{Binding Button22.(Grid.Row)}
添付プロパティはかっこを使用して指定します。 プロパティが XAML 名前空間で宣言されていない場合は、ドキュメントの先頭にあるコード名前空間にマップする必要がある xml 名前空間のプレフィックスを付けます。
キャスティング {x:Bind groups[0].(data:SampleDataGroup.Title)}
-
{Binding} には必要ありません。
キャストはかっこを使用して指定します。 プロパティが XAML 名前空間で宣言されていない場合は、ドキュメントの先頭にあるコード名前空間にマップする必要がある xml 名前空間のプレフィックスを付けます。
コンバータ {x:Bind IsShown, Converter={StaticResource BoolToVisibility}}
-
{Binding IsShown, Converter={StaticResource BoolToVisibility}}
コンバーターは、Page/ResourceDictionary のルートまたは App.xaml で宣言する必要があります。
ConverterParameter、ConverterLanguage {x:Bind IsShown, Converter={StaticResource BoolToVisibility}, ConverterParameter=One, ConverterLanguage=fr-fr}
-
{Binding IsShown, Converter={StaticResource BoolToVisibility}, ConverterParameter=One, ConverterLanguage=fr-fr}
コンバーターは、Page/ResourceDictionary のルートまたは App.xaml で宣言する必要があります。
TargetNullValue {x:Bind Name, TargetNullValue=0}
-
{Binding Name, TargetNullValue=0}
バインド式のリーフが null の場合に使用されます。 文字列値には単一引用符を使用します。
FallbackValue {x:Bind Name, FallbackValue='empty'}
-
{Binding Name, FallbackValue='empty'}
バインディングのパスの一部 (リーフを除く) が null の場合に使用されます。
ElementName {x:Bind slider1.Value}
-
{Binding Value, ElementName=slider1}
{x:Bind} では、フィールドにバインドします。パスは既定で Page にルート化されているため、名前付き要素はフィールドを介してアクセスできます。
RelativeSource: Self <Rectangle x:Name="rect1" Width="200" Height="{x:Bind rect1.Width}" ... />
-
<Rectangle Width="200" Height="{Binding Width, RelativeSource={RelativeSource Self}}" ... />
{x:Bind} を使用して、要素に名前を付け、Path でその名前を使用します。
RelativeSource: TemplatedParent {x:Bind} には必要ありません
-
{Binding <path>, RelativeSource={RelativeSource TemplatedParent}}
ControlTemplate の {x:Bind} TargetType では、テンプレートの親へのバインドを示します。 {Binding} の場合、通常のテンプレート バインドは、ほとんどの用途でコントロール テンプレートで使用できます。 ただし、コンバーターまたは双方向バインディングを使用する必要がある場合は TemplatedParent を使用します。<
情報源 {x:Bind} には必要ありません
-
<ListView ItemsSource="{Binding Orders, Source={StaticResource MyData}}"/>
{x:Bind} の場合、名前付き要素を直接使用し、プロパティまたは静的パスを使用できます。
Mode {x:Bind Name, Mode=OneWay}
-
{Binding Name, Mode=TwoWay}
モードには、OneTime、OneWay、または TwoWay を指定できます。 {x:Bind} の既定値は OneTime です。{Binding} の既定値は OneWay です。
UpdateSourceTrigger {x:Bind Name, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}
-
{Binding UpdateSourceTrigger=PropertyChanged}
UpdateSourceTrigger には、Default、LostFocus、または PropertyChanged を指定できます。 {x:Bind} は UpdateSourceTrigger=Explicit をサポートしていません。 {x:Bind} では、TextBox.Text を除くすべてのケースで PropertyChanged 動作が使用され、LostFocus 動作が使用されます。