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++/WinRT 中实现可观测属性,以及如何将 XAML 控件绑定到这些属性(如需背景信息,请参阅数据绑定)。This topic shows how to implement observable properties in C++/WinRT, and how to bind XAML controls to them (for background info, see Data binding).

重要

有关支持你了解如何利用 C++/WinRT 来使用和创作运行时类的基本概述和术语,请参阅通过 C++/WinRT 使用 API通过 C++/WinRT 创作 APIFor 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 的运行时类有一个名为“标题”的属性 。Let's say that a runtime class named BookSku has a property named Title. 如果每当“标题”的值发生更改时,BookSku 都会引发 INotifyPropertyChanged::PropertyChanged 事件,这表示“标题”为一个可观测属性 。If BookSku raises the INotifyPropertyChanged::PropertyChanged event whenever the value of Title changes, then that means that 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. 此类元素或控件会检索更新的值,然后自行更新以显示新值,从而处理事件。Such an element or control handles the event by retrieving the updated value(s), and then updating itself to show the new value.

备注

有关安装和使用 C++/WinRT Visual Studio 扩展 (VSIX) 和 NuGet 包(两者共同提供项目模板,并生成支持)的信息,请参阅适用于 C++/WinRT 的 Visual Studio 支持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)”项目,然后将其命名为 Bookstore。Create a Blank App (C++/WinRT) project, and name it Bookstore. 请确保未选中“将解决方案和项目放在同一目录中”。Make sure that Place solution and project in the same directory is unchecked. 面向 Windows SDK 的最新正式发布(非预览)版本。Target the latest generally-available (that is, not preview) version of the Windows SDK.

我们将创作新类来表示具有可观测标题属性的书籍。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 the new item 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
    {
        BookSku(String title);
        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. 若要使应用程序通过 Visual Studio 和 Microsoft Store 用于验证提交的 Windows 应用认证工具包测试,使 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. 生成尚不会完全成功,但它将为我们执行一些必要操作。The build won't entirely succeed yet, but it will do some necessary things for us. 尤其是在生成过程中,会运行 midl.exe 工具来创建 Windows 运行时元数据文件 (\Bookstore\Debug\Bookstore\Unmerged\BookSku.winmd),描述该运行时类。Specifically, 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.cppThose 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.

实现 BookSkuImplement BookSku

现在,让我们打开 \Bookstore\Bookstore\BookSku.hBookSku.cpp 并实现运行时类。Now let's open \Bookstore\Bookstore\BookSku.h and BookSku.cpp and implement our runtime class. 首先,你将在 BookSku.hBookSku.cpp 的顶部看到 static_assert(需要删除)。First, you'll see a static_assert at the top of BookSku.h and BookSku.cpp, which you'll need to remove.

接下来,在 BookSku.h 中进行以下更改。Next, in BookSku.h, make these changes.

  • 在默认构造函数中,将 = default 更改为 = deleteOn the default constructor, change = default to = delete. 原因是我们不需要默认构造函数。That's because we don't want a default constructor.
  • 添加私有成员以存储标题字符串。Add a private member to store the title string. 请注意,我们具有一个采用 winrt::hstring 值的构造函数。Note that we have a constructor that takes a winrt::hstring value. 此值为标题字符串。That value is the title string.
  • 为将在标题更改时引发的事件添加另一个私有成员。Add another private member 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;
    };
}
namespace winrt::Bookstore::factory_implementation
{
    struct BookSku : BookSkuT<BookSku, implementation::BookSku>
    {
    };
}

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, then 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.

如果想要检查它,则项目将立即再次生成。The project will build again now, if you want to check that.

声明并实现 BookstoreViewModelDeclare 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. 另请参阅将运行时类重构到 Midl 文件 (.idl) 中But also see Factoring runtime classes into Midl files (.idl).

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

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

保存并生成(生成尚不会完全成功,但我们进行生成的原因是要再次得到存根文件)。Save and build (the build won't entirely succeed yet, but the reason we're building is to generate stub files again).

BookstoreViewModel.hBookstoreViewModel.cppGenerated Files\sources 文件夹复制到项目文件夹中,然后将其包含在项目中。Copy BookstoreViewModel.h and BookstoreViewModel.cpp from the Generated Files\sources folder into the project folder, and include them in the project. 打开这些文件(再次删除 static_assert),并实现如下所示的运行时类。Open those files (removing the static_assert again), 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). 我们将从默认构造函数中删除 = defaultAnd we're removing = default from the default constructor.

备注

在下面的 BookstoreViewModel.hBookstoreViewModel.cpp 列表中,代码阐释了构造 m_bookSku 数据成员的默认方式。In the listings below for BookstoreViewModel.h and BookstoreViewModel.cpp, the code illustrates the default way of constructing the m_bookSku data member. 这是回溯到 C++/WinRT 初版的方法,最好要至少熟悉该模式。That's the method that dates back to the first release of C++/WinRT, and it's a good idea to be at least familiar with the pattern. 在 C++/WinRT 版本 2.0 及更高版本中,有一种优化的构造形式可供你使用,它被称作“统一构造”(请参见 C++/WinRT 2.0 中的新增功能和更改)。With C++/WinRT version 2.0 and later, there's an optimized form of construction available to you known as uniform construction (see News, and changes, in C++/WinRT 2.0). 在本主题的稍后部分,我们将展示统一构造的示例。Later in this topic, we'll show an example of uniform construction.

// 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 };
    };
}
namespace winrt::Bookstore::factory_implementation
{
    struct BookstoreViewModel : BookstoreViewModelT<BookstoreViewModel, implementation::BookstoreViewModel>
    {
    };
}
// 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.

现在将再次生成项目。The project will build again now.

将类型 BookstoreViewModel 的属性添加到 MainPageAdd a property of type BookstoreViewModel to MainPage

打开 MainPage.idl,这将声明表示主 UI 页面的运行时类。Open MainPage.idl, which declares the runtime class that represents our main UI page.

  • 添加 import 指令来导入 BookstoreViewModel.idlAdd an import directive to import BookstoreViewModel.idl.
  • 添加一个类型为 BookstoreViewModel、名为 MainViewModel 的只读属性 。Add a read-only property named MainViewModel, of type BookstoreViewModel.
  • 删除 MyProperty 属性。Remove the MyProperty property.
// 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 entirely succeed in building yet, 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'.

如果未包含 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.cppTo 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 中,执行以下步骤。In \Bookstore\Bookstore\MainPage.h, perform these steps.

  • 包含 BookstoreViewModel.h,它为 BookstoreViewModel 声明实现类型(即 winrt::Bookstore::implementation::BookstoreViewModel) 。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).
  • 实现类型与应用程序位于同一项目(编译单元),因此我们通过采用 std::nullptr_t 的构造函数重载来构造 m_mainViewModel。The implementation type is in the same project (compilation unit) as the application, so we construct m_mainViewModel via the constructor overload that takes std::nullptr_t.
  • 删除 MyProperty 属性。Remove the MyProperty property.

备注

在下面的 MainPage.hMainPage.cpp 的两个列表中,代码阐释了构造 m_mainViewModel 数据成员的默认方式。In the pair of listings below for MainPage.h and MainPage.cpp, the code illustrates the default way of constructing the m_mainViewModel data member. 在以下部分中,我们将展示改用统一构造的版本。In the section that follows, we'll show a version that uses uniform construction instead.

// 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 中进行以下更改。In \Bookstore\Bookstore\MainPage.cpp, as shown in the listing below, make the following changes.

  • 调用 winrt::make(具有 BookstoreViewModel 实现类型)将投影的 BookstoreViewModel 类型的新实例分配到 m_mainViewModel 。Call winrt::make (with the BookstoreViewModel implementation type) to assign a new instance of the projected BookstoreViewModel type to m_mainViewModel. 正如前文所述,BookstoreViewModel 构造函数会创建一个新的 BookSku 对象作为专用数据成员,并在一开始将其标题设置为 L"Atticus"As we saw above, the BookstoreViewModel constructor creates a new BookSku object as a private data member, setting its title initially to L"Atticus".
  • 在按钮的事件处理程序 (ClickHandler) 中,将书籍的标题更新为其发布的标题。In the button's event handler (ClickHandler), update the book's title to its published title.
  • 针对 MainViewModel 属性实现访问器。Implement the accessor for the MainViewModel property.
  • 删除 MyProperty 属性。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;
    }
}

统一构造Uniform construction

若要使用统一构造而不是 winrt::make,请在 MainPage.h 中声明和初始化 m_mainViewModel;此操作只需一步,如下所示。To use uniform construction instead of winrt::make, in MainPage.h declare and initialize m_mainViewModel in just one step, as shown below.

// MainPage.h
...
#include "BookstoreViewModel.h"
...
struct MainPage : MainPageT<MainPage>
{
    ...
private:
    Bookstore::BookstoreViewModel m_mainViewModel;
};
...

接下来,在 MainPage.cpp 中的 MainPage 构造函数中,无需使用代码 m_mainViewModel = winrt::make<Bookstore::implementation::BookstoreViewModel>();And then, in the MainPage constructor in MainPage.cpp, there's no need for the code m_mainViewModel = winrt::make<Bookstore::implementation::BookstoreViewModel>();.

有关统一构造的详细信息,请参阅选择加入统一构造和直接实现访问For more info about uniform construction, and code examples, see Opt in to uniform construction, and direct implementation access.

将按钮绑定到“标题”属性Bind the button to the Title property

打开 MainPage.xaml,其中包含主 UI 页面的 XAML 标记。Open MainPage.xaml, which contains the XAML markup for our main UI page. 如下表所示,删除按钮中的名称,并将其 Content 属性值从文字更改为绑定表达式。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 事件处理程序。Click the button to execute the Click event handler. 该处理程序调用书籍的标题转变器函数;该转变器引发了让 UI 知道“标题”属性已发生更改的事件;而且按钮重新查询了该属性的值以更新其自己的“内容”值 。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.

配合使用 {Binding} 标记扩展与 C++/WinRTUsing the {Binding} markup extension with C++/WinRT

对于当前发布的 C++/WinRT 版本,为了能够使用 {Binding} 标记扩展,需要实现 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.

元素间的绑定Element-to-element binding

可以将一个 XAML 元素的属性绑定到另一个 XAML 元素的属性。You can bind the property of one XAML element to the property of another XAML element. 下面是在标记中进行的该操作的一个示例。Here's an example of how that looks in markup.

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

需要在 Midl 文件 (.idl) 中将命名的 XAML 实体 myTextBox 声明为只读属性。You'll need to declare the named XAML entity myTextBox as a read-only property in your Midl file (.idl).

// MainPage.idl
runtimeclass MainPage : Windows.UI.Xaml.Controls.Page
{
    MainPage();
    Windows.UI.Xaml.Controls.TextBox myTextBox{ get; };
}

必须这样做的原因是:Here's the reason for this necessity. XAML 编译器进行验证所需的所有类型(包括在 {x:Bind} 中使用的类型)都是从 Windows 元数据 (WinMD) 读取的。All types that the XAML compiler needs to validate (including those used in {x:Bind}) are read from Windows Metadata (WinMD). 你只需将只读属性添加到 Midl 文件即可。All you need to do is to add the read-only property to your Midl file. 请勿实现它,因为自动生成的 XAML 代码隐藏会为你提供实现。Don't implement it, because the autogenerated XAML code-behind provides the implementation for you.

使用 XAML 标记中的对象Consuming objects from XAML markup

以 XAML {x:Bind} 标记扩展形式使用的所有实体必须在 IDL 中以公开方式公开。All entities consumed by using the XAML {x:Bind} markup extension must be exposed publicly in IDL. 另外,如果 XAML 标记包含对另一元素的引用,且该引用也存在于标记中,则该标记的 getter 必须存在于 IDL 中。Furthermore, if XAML markup contains a reference to another element that's also in markup, then the getter for that markup must be present in IDL.

<Page x:Name="MyPage">
    <StackPanel>
        <CheckBox x:Name="UseCustomColorCheckBox" Content="Use custom color"
             Click="UseCustomColorCheckBox_Click" />
        <Button x:Name="ChangeColorButton" Content="Change color"
            Click="{x:Bind ChangeColorButton_OnClick}"
            IsEnabled="{x:Bind UseCustomColorCheckBox.IsChecked.Value, Mode=OneWay}"/>
    </StackPanel>
</Page>

ChangeColorButton 元素通过绑定引用 UseCustomColorCheckBox 元素。The ChangeColorButton element refers to the UseCustomColorCheckBox element via binding. 因此,此页的 IDL 必须声明一个名为 UseCustomColorCheckBox 的只读属性,然后它才能供绑定访问。So the IDL for this page must declare a read-only property named UseCustomColorCheckBox in order for it to be accessible to binding.

UseCustomColorCheckBox 的点击事件处理程序委托使用经典的 XAML 委托语法,因此不需要在 IDL 中有一个条目,只需在实现类中处于公开状态即可。The click event handler delegate for UseCustomColorCheckBox uses classic XAML delegate syntax, so that doesn't need an entry in the IDL; it just needs to be public in your implementation class. 另一方面,ChangeColorButton 也有一个 {x:Bind} 点击事件处理程序,该程序也必须进入 IDL 中。On the other hand, ChangeColorButton also has an {x:Bind} click event handler, which must also go into the IDL.

runtimeclass MyPage : Windows.UI.Xaml.Controls.Page
{
    MyPage();

    // These members are consumed by binding.
    void ChangeColorButton_OnClick();
    Windows.UI.Xaml.Controls.CheckBox UseCustomColorCheckBox{ get; };
}

不需为 UseCustomColorCheckBox 属性提供一个实现。You don't need to provide an implementation for the UseCustomColorCheckBox property. XAML 代码生成器会为你这样做。The XAML code generator does that for you.

绑定到布尔值Binding to Boolean

可以在诊断模式下这样做。You might do this in a diagnostic mode.

这会在 C++/CX 中显示 truefalse,但在 C++/WinRT 中则显示 Windows.Foundation.IReference`1This shows true or false in C++/CX, but shows Windows.Foundation.IReference`1 in C++/WinRT.

绑定到布尔值时使用 x:BindUse x:Bind when binding to a Boolean.

<TextBlock Text="{x:Bind CanPair}"/>

重要的 APIImportant APIs