XAML コントロール: C++/WinRT プロパティへのバインドXAML controls; bind to a C++/WinRT property

XAML コントロールに効果的にバインドできるプロパティは、監視可能なプロパティと呼ばれます。A property that can be effectively bound to a XAML control is known as an observable property. この概念は、オブザーバー パターンと呼ばれるソフトウェアの設計パターンに基づいています。This idea is based on the software design pattern known as the observer pattern. このトピックで監視可能なプロパティを実装する方法を示しています。 C +/cli WinRT、および XAML コントロールをバインドする方法。This topic shows how to implement observable properties in C++/WinRT, and how to bind XAML controls to them.

重要

C++/WinRT でランタイム クラスを使用および作成する方法についての理解をサポートするために重要な概念と用語については、「C++/WinRT での API の使用」と「C++/WinRT での作成者 API」を参照してください。For essential concepts and terms that support your understanding of how to consume and author runtime classes with C++/WinRT, see Consume APIs with C++/WinRT and Author APIs with C++/WinRT.

プロパティの監視可能とはどういう意味ですか?What does observable mean for a property?

たとえば、BookSku という名前のランタイム クラスに Title という名前のプロパティがあるとします。Let's say that a runtime class named BookSku has a property named Title. Title の値が変わるたびに BookSkuINotifyPropertyChanged::PropertyChanged イベントを発生させることを選択する場合、Title は監視可能なプロパティです。If BookSku chooses to raise the INotifyPropertyChanged::PropertyChanged event whenever the value of Title changes, then Title is an observable property. そのプロパティ (あれば) のどれが監視可能かを決定するのは BookSku の動作 (イベントを発生させるまたは発生させない) です。It's the behavior of BookSku (raising or not raising the event) that determines which, if any, of its properties are observable.

XAML テキスト要素、またはコントロールでは、更新された値を取得して、新しい値を表示するためにそれ自体を更新することで、これらのイベントをバインドし、処理することができます。A XAML text element, or control, can bind to, and handle, these events by retrieving the updated value(s) and then updating itself to show the new value.

注意

インストールと使用について、 C++WinRT Visual Studio Extension (VSIX) と (をまとめてプロジェクト テンプレートを提供し、ビルドのサポート)、NuGet パッケージを参照してください。 Visual Studio のサポートC++/WinRTします。For info about installing and using the C++/WinRT Visual Studio Extension (VSIX) and the NuGet package (which together provide project template and build support), see Visual Studio support for C++/WinRT.

空のアプリ (Bookstore) を作成するCreate a Blank App (Bookstore)

まず、Microsoft Visual Studio で、新しいプロジェクトを作成します。Begin by creating a new project in Microsoft Visual Studio. 作成、空のアプリ (C++/WinRT) プロジェクト、および名前を付けます書店します。Create a Blank App (C++/WinRT) project, and name it Bookstore.

監視可能なタイトルのプロパティを持つブックを表すための新しいクラスを作成します。We're going to author a new class to represent a book that has an observable title property. 同じコンパイル ユニット内のクラスを作成および使用しています。We're authoring and consuming the class within the same compilation unit. ただし、XAML からこのクラスへバインドできるようにしたいため、ランタイム クラスにします。But we want to be able to bind to this class from XAML, and for that reason it's going to be a runtime class. また、この作成と使用のどちらにも C++/WinRT を使用します。And we're going to use C++/WinRT to both author and consume it.

新しいランタイム クラスの作成の最初の手順では、新しい Midl ファイル (.idl) 項目をプロジェクトに追加します。The first step in authoring a new runtime class is to add a new Midl File (.idl) item to the project. BookSku.idl」という名前を入力します。Name it BookSku.idl. BookSku.idl の既定のコンテンツを削除し、このランタイム クラスの宣言に貼り付けます。Delete the default contents of BookSku.idl, and paste in this runtime class declaration.

// BookSku.idl
namespace Bookstore
{
    runtimeclass BookSku : Windows.UI.Xaml.Data.INotifyPropertyChanged
    {
        String Title;
    }
}

注意

ビュー モデル クラス—実際には、アプリケーションで宣言された任意のランタイム クラス—基底クラスから派生していない必要があります。Your view model classes—in fact, any runtime class that you declare in your application—need not derive from a base class. BookSku上で宣言されたクラスがその例を示します。The BookSku class declared above is an example of that. インターフェイスを実装しますが、任意の基本クラスから派生していません。It implements an interface, but it doesn't derive from any base class.

アプリケーションで宣言された任意のランタイム クラスをベースからの派生クラスと呼ばれる、コンポーザブルクラス。Any runtime class that you declare in the application that does derive from a base class is known as a composable class. 構成可能なクラスに関する制約があります。And there are constraints around composable classes. アプリケーションに渡す、 Windows アプリ認定キットVisual Studio では、Microsoft Store での送信を検証するために使用するテスト (ため Microsoft Store に正常に取り込まれるアプリケーションの)、コンポーザブル クラスは、Windows の基本クラスから最終的に派生する必要があります。For an application to pass the Windows App Certification Kit tests used by Visual Studio and by the Microsoft Store to validate submissions (and therefore for the application to be successfully ingested into the Microsoft Store), a composable class must ultimately derive from a Windows base class. つまり、継承階層の非常にルートにあるクラスは、Windows.* 名前空間で生成された型である必要があります。Meaning that the class at the very root of the inheritance hierarchy must be a type originating in a Windows.* namespace. ランタイム クラスを基底クラスから派生する必要は場合—を実装する例をBindableBaseクラスから派生するは、ビュー モデルのすべての—から派生できます Windows.UI.Xaml.DependencyObjectします。If you do need to derive a runtime class from a base class—for example, to implement a BindableBase class for all of your view models to derive from—then you can derive from Windows.UI.Xaml.DependencyObject.

ビュー モデルは、ビューの抽象化と表示 (XAML マークアップ) に直接バインドされているためです。A view model is an abstraction of a view, and so it's bound directly to the view (the XAML markup). データ モデルは、データの抽象化と、ビュー モデルからのみ使用され、XAML に直接バインドされていないことができます。A data model is an abstraction of data, and it's consumed only from your view models, and not bound directly to XAML. そのため、ランタイム クラス、としてではなく C++ 構造体またはクラスとしては、データ モデルを宣言できます。So, you can declare your data models not as runtime classes, but as C++ structs or classes. MIDL、内で宣言する必要はありませんし、自由に好きな継承階層を使用します。They don't need to be declared in MIDL, and you're free to use whatever inheritance hierarchy you like.

ファイルを保存し、プロジェクトをビルドします。Save the file and build the project. ビルド プロセス中に、midl.exe ツールが実行されて、ランタイム クラスを記述する Windows ランタイム メタデータ ファイル (\Bookstore\Debug\Bookstore\Unmerged\BookSku.winmd) が作成されます。During the build process, the midl.exe tool is run to create a Windows Runtime metadata file (\Bookstore\Debug\Bookstore\Unmerged\BookSku.winmd) describing the runtime class. 次に、cppwinrt.exe ツールが実行され、ランタイム クラスの作成と使用をサポートするソース コード ファイルが生成されます。Then, the cppwinrt.exe tool is run to generate source code files to support you in authoring and consuming your runtime class. これらのファイルには、IDL で宣言した BookSku ランタイム クラスの実装を開始するためのスタブが含まれています。These files include stubs to get you started implementing the BookSku runtime class that you declared in your IDL. これらのスタブは \Bookstore\Bookstore\Generated Files\sources\BookSku.hBookSku.cpp です。Those stubs are \Bookstore\Bookstore\Generated Files\sources\BookSku.h and BookSku.cpp.

プロジェクト ノードを右クリックし、をクリックしてファイル エクスプ ローラーでフォルダーを開くします。Right-click the project node and click Open Folder in File Explorer. これは、ファイル エクスプ ローラーでプロジェクト フォルダーを開きます。This opens the project folder in File Explorer. スタブ ファイルをコピー、BookSku.hBookSku.cppから、\Bookstore\Bookstore\Generated Files\sources\フォルダー、プロジェクト フォルダーにある\Bookstore\Bookstore\します。There, copy the stub files BookSku.h and BookSku.cpp from the \Bookstore\Bookstore\Generated Files\sources\ folder and into the project folder, which is \Bookstore\Bookstore\. ソリューション エクスプ ローラー、プロジェクト ノードを選択したことを確認しますすべてのファイルがオンにします。In Solution Explorer, with the project node selected, make sure Show All Files is toggled on. コピーしたスタブ ファイルを右クリックし、[プロジェクトに含める] をクリックします。Right-click the stub files that you copied, and click Include In Project.

BookSku を実装するImplement BookSku

ここで、\Bookstore\Bookstore\BookSku.hBookSku.cpp を開いてランタイム クラスを実装してみましょう。Now, let's open \Bookstore\Bookstore\BookSku.h and BookSku.cpp and implement our runtime class. BookSku.h で、winrt::hstring を取るコンストラクター、タイトル文字列を格納するためのプライベート メンバー、およびタイトルが変更されたときに発生させるイベントのための別のプライベート メンバーを追加します。In BookSku.h, add a constructor that takes a winrt::hstring, a private member to store the title string, and another for the event that we'll raise when the title changes. これらの変更を行った後、BookSku.hようになります。After making these changes, your BookSku.h will look like this.

// BookSku.h
#pragma once
#include "BookSku.g.h"

namespace winrt::Bookstore::implementation
{
    struct BookSku : BookSkuT<BookSku>
    {
        BookSku() = delete;
        BookSku(winrt::hstring const& title);

        winrt::hstring Title();
        void Title(winrt::hstring const& value);
        winrt::event_token PropertyChanged(Windows::UI::Xaml::Data::PropertyChangedEventHandler const& value);
        void PropertyChanged(winrt::event_token const& token);
    
    private:
        winrt::hstring m_title;
        winrt::event<Windows::UI::Xaml::Data::PropertyChangedEventHandler> m_propertyChanged;
    };
}

BookSku.cpp では、次のように関数を実装します。In BookSku.cpp, implement the functions like this.

// BookSku.cpp
#include "pch.h"
#include "BookSku.h"
#include "BookSku.g.cpp"

namespace winrt::Bookstore::implementation
{
    BookSku::BookSku(winrt::hstring const& title) : m_title{ title }
    {
    }

    winrt::hstring BookSku::Title()
    {
        return m_title;
    }

    void BookSku::Title(winrt::hstring const& value)
    {
        if (m_title != value)
        {
            m_title = value;
            m_propertyChanged(*this, Windows::UI::Xaml::Data::PropertyChangedEventArgs{ L"Title" });
        }
    }

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

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

タイトルかどうか、値が設定されていますが、現在の値を異なるを確認しましたミューテーターの関数。In the Title mutator function, we check whether a value is being set that's different from the current value. そのため、タイトルを更新するとを発生させることも、 INotifyPropertyChanged::PropertyChanged 変更されたプロパティの名前と同じ引数を持つイベント。And, if so, we update the title and also raise the INotifyPropertyChanged::PropertyChanged event with an argument equal to the name of the property that has changed. これにより、ユーザー インターフェイス (UI) はどのプロパティの値を再クエリするのか分かるようになります。This is so that the user-interface (UI) will know which property's value to re-query.

BookstoreViewModel を宣言および実装するDeclare and implement BookstoreViewModel

メイン XAML ページが、メイン ビュー モデルにバインドします。Our main XAML page is going to bind to a main view model. またそのビュー モデルは、BookSku 型のいずれかを含む、いくつかのプロパティを持つようになります。And that view model is going to have several properties, including one of type BookSku. この手順では、メイン ビュー モデル ランタイム クラスを宣言および実装します。In this step, we'll declare and implement our main view model runtime class.

BookstoreViewModel.idl という名前の新しい Midl ファイル (.idl) 項目を追加します。Add a new Midl File (.idl) item named BookstoreViewModel.idl.

// BookstoreViewModel.idl
import "BookSku.idl";

namespace Bookstore
{
    runtimeclass BookstoreViewModel
    {
        BookSku BookSku{ get; };
    }
}

保存してビルドします。Save and build. Generated Files\sources フォルダーから BookstoreViewModel.hBookstoreViewModel.cpp をプロジェクト フォルダーにコピーし、プロジェクトに追加します。Copy BookstoreViewModel.h and BookstoreViewModel.cpp from the Generated Files\sources folder into the project folder, and include them in the project. これらのファイルを開き、次に示すように、ランタイム クラスを実装します。Open those files and implement the runtime class as shown below. 注方法で、 BookstoreViewModel.h、含まれていますBookSku.hの実装の種類を宣言するBookSku (これはwinrt::Bookstore::implementation::BookSku)。Note how, in BookstoreViewModel.h, we're including BookSku.h, which declares the implementation type for BookSku (which is winrt::Bookstore::implementation::BookSku). 削除する予定していると= default既定のコンス トラクターから。And we're removing = default from the default constructor.

// BookstoreViewModel.h
#pragma once
#include "BookstoreViewModel.g.h"
#include "BookSku.h"

namespace winrt::Bookstore::implementation
{
    struct BookstoreViewModel : BookstoreViewModelT<BookstoreViewModel>
    {
        BookstoreViewModel();

        Bookstore::BookSku BookSku();

    private:
        Bookstore::BookSku m_bookSku{ nullptr };
    };
}
// BookstoreViewModel.cpp
#include "pch.h"
#include "BookstoreViewModel.h"
#include "BookstoreViewModel.g.cpp"

namespace winrt::Bookstore::implementation
{
    BookstoreViewModel::BookstoreViewModel()
    {
        m_bookSku = winrt::make<Bookstore::implementation::BookSku>(L"Atticus");
    }

    Bookstore::BookSku BookstoreViewModel::BookSku()
    {
        return m_bookSku;
    }
}

注意

m_bookSku射影の型は、(winrt::Bookstore::BookSku) とで使用するテンプレート パラメーター winrt::make が、実装の種類 (winrt::Bookstore::implementation::BookSku)。The type of m_bookSku is the projected type (winrt::Bookstore::BookSku), and the template parameter that you use with winrt::make is the implementation type (winrt::Bookstore::implementation::BookSku). この場合も、[make] は投影型のインスタンスを返します。Even so, make returns an instance of the projected type.

BookstoreViewModel 型のプロパティを MainPage に追加するAdd a property of type BookstoreViewModel to MainPage

MainPage.idl を開きます。ここでメインの UI ページを表すランタイム クラスを宣言しています。Open MainPage.idl, which declares the runtime class that represents our main UI page. インポート ステートメントを追加して BookstoreViewModel.idl をインポートし、BookstoreViewModel 型の MainViewModel という名前の読み取り専用プロパティを追加します。Add an import statement to import BookstoreViewModel.idl, and add a read-only property named MainViewModel of type BookstoreViewModel. 削除しても、 MyPropertyプロパティ。Also remove the MyProperty property. また、importディレクティブでは、以下のリスト。Also note the import directive in the listing below.

// MainPage.idl
import "BookstoreViewModel.idl";

namespace Bookstore
{
    runtimeclass MainPage : Windows.UI.Xaml.Controls.Page
    {
        MainPage();
        BookstoreViewModel MainViewModel{ get; };
    }
}

ファイルを保存します。Save the file. 現時点では、完了するまで、プロジェクトをビルドしませんをソース コード ファイルを再生成しているため、有用なことは、今すぐ構築、 MainPageランタイム クラスが実装されている (\Bookstore\Bookstore\Generated Files\sources\MainPage.hMainPage.cpp)。The project won't build to completion at the moment, but building now is a useful thing to do because it regenerates the source code files in which the MainPage runtime class is implemented (\Bookstore\Bookstore\Generated Files\sources\MainPage.h and MainPage.cpp). これを今すぐビルドしてみてください。So go ahead and build now. この段階で表示するビルド エラーが 'MainViewModel': 'winrt::Bookstore::implementation::MainPage' のメンバーではないします。The build error you can expect to see at this stage is 'MainViewModel': is not a member of 'winrt::Bookstore::implementation::MainPage'.

Include を省略した場合BookstoreViewModel.idl(の一覧を参照してくださいMainPage.idl上)、エラーが表示されますを指定してください<"MainViewModel"近くIf you omit the include of BookstoreViewModel.idl (see the listing of MainPage.idl above), then you'll see the error expecting < near "MainViewModel". 別のヒントは、同じ名前空間のすべての種類のままにすることを確認する: コードの一覧に表示される名前空間。Another tip is to make sure that you leave all types in the same namespace: the namespace that's shown in the code listings.

表示される予定ですエラーを解決するようになりましたする必要がありますのアクセサーのスタブをコピー、 MainViewModel生成されたファイルからのプロパティ (\Bookstore\Bookstore\Generated Files\sources\MainPage.hMainPage.cpp) に\Bookstore\Bookstore\MainPage.hMainPage.cppします。To resolve the error that we expect to see, you'll now need to copy the accessor stubs for the MainViewModel property out of the generated files (\Bookstore\Bookstore\Generated Files\sources\MainPage.h and MainPage.cpp) and into \Bookstore\Bookstore\MainPage.h and MainPage.cpp. そのための手順は次に説明します。The steps to do that are described next.

\Bookstore\Bookstore\MainPage.h、含めるBookstoreViewModel.hの実装の種類を宣言するBookstoreViewModel (これはwinrt::Bookstore::implementation::BookstoreViewModel)。In \Bookstore\Bookstore\MainPage.h, include BookstoreViewModel.h, which declares the implementation type for BookstoreViewModel (which is winrt::Bookstore::implementation::BookstoreViewModel). ビュー モデルを格納するプライベート メンバーを追加します。Add a private member to store the view model. プロパティのアクセサー関数 (およびメンバー m_mainViewModel) が、射影された型の観点から実装されることに注意してください。 BookstoreViewModel (これはBookstore::BookstoreViewModel)。Note that the property accessor function (and the member m_mainViewModel) is implemented in terms of the projected type for BookstoreViewModel (which is Bookstore::BookstoreViewModel). M_mainViewModel を受け取るコンス トラクター オーバー ロードを使用して作成するため、アプリケーションと同じプロジェクト (コンパイル単位) での実装の種類はnullptr_tします。The implementation type is in the same project (compilation unit) as the application, so we construct m_mainViewModel via the constructor overload that takes nullptr_t. 削除しても、 MyPropertyプロパティ。Also remove the MyProperty property.

// MainPage.h
...
#include "BookstoreViewModel.h"
...
namespace winrt::Bookstore::implementation
{
    struct MainPage : MainPageT<MainPage>
    {
        MainPage();

        Bookstore::BookstoreViewModel MainViewModel();

        void ClickHandler(Windows::Foundation::IInspectable const&, Windows::UI::Xaml::RoutedEventArgs const&);

    private:
        Bookstore::BookstoreViewModel m_mainViewModel{ nullptr };
    };
}
...

\Bookstore\Bookstore\MainPage.cpp、呼び出す winrt::make (実装の種類) で m_mainViewModel に射影された型の新しいインスタンスを割り当てる。In \Bookstore\Bookstore\MainPage.cpp, call winrt::make (with the implementation type) to assign a new instance of the projected type to m_mainViewModel. ブックのタイトルの初期値を割り当てます。Assign an initial value for the book's title. MainViewModel プロパティのアクセサーを実装します。Implement the accessor for the MainViewModel property. 最後に、ボタンのイベント ハンドラーでブックのタイトルを更新します。And finally, update the book's title in the button's event handler. 削除しても、 MyPropertyプロパティ。Also remove the MyProperty property.

// MainPage.cpp
#include "pch.h"
#include "MainPage.h"
#include "MainPage.g.cpp"

using namespace winrt;
using namespace Windows::UI::Xaml;

namespace winrt::Bookstore::implementation
{
    MainPage::MainPage()
    {
        m_mainViewModel = winrt::make<Bookstore::implementation::BookstoreViewModel>();
        InitializeComponent();
    }

    void MainPage::ClickHandler(Windows::Foundation::IInspectable const& /* sender */, Windows::UI::Xaml::RoutedEventArgs const& /* args */)
    {
        MainViewModel().BookSku().Title(L"To Kill a Mockingbird");
    }

    Bookstore::BookstoreViewModel MainPage::MainViewModel()
    {
        return m_mainViewModel;
    }
}

Title プロパティにボタンをバインドするBind the button to the Title property

メイン UI ページの XAML マークアップが含まれている MainPage.xaml を開きます。Open MainPage.xaml, which contains the XAML markup for our main UI page. 次の一覧に示すように、ボタンから名前を削除し、変更、コンテンツバインディング式にリテラルからプロパティ値。As shown in the listing below, remove the name from the button, and change its Content property value from a literal to a binding expression. バインド式の Mode=OneWay プロパティをメモします (ビュー モデルから UI へ一方向)。Note the Mode=OneWay property on the binding expression (one-way from the view model to the UI). そのプロパティが無いと、UI はプロパティの変更イベントに応答しません。Without that property, the UI will not respond to property changed events.

<Button Click="ClickHandler" Content="{x:Bind MainViewModel.BookSku.Title, Mode=OneWay}"/>

ここでプロジェクトをビルドして実行します。Now build and run the project. ボタンをクリックしてクリック イベント ハンドラーを実行します。Click the button to execute the Click event handler. そのハンドラーがブックのタイトル ミューテーター関数を呼び出します。そのミューテーターがイベントを発生させるので、UI は Title プロパティが変更されたことがわかります。ボタンはそのプロパティの値を再クエリして、そのボタンの Content 値を更新します。That handler calls the book's title mutator function; that mutator raises an event to let the UI know that the Title property has changed; and the button re-queries that property's value to update its own Content value.

{バインド} マークアップ拡張機能を使用して c++/cli WinRTUsing the {Binding} markup extension with C++/WinRT

現在リリースされているバージョンの C++/cli を実装する必要があります {binding} マークアップ拡張機能を使用するには、WinRT、 ICustomPropertyProviderICustomPropertyインターフェイス。For the currently released version of C++/WinRT, in order to be able to use the {Binding} markup extension you'll need to implement the ICustomPropertyProvider and ICustomProperty interfaces.

重要な APIImportant APIs