XAML 项控件;绑定到 C++/WinRT 集合

可以有效地绑定到 XAML 项控件的集合称为 可观察 集合。 此想法基于称为 观察程序模式的软件设计模式。 本主题演示如何在 C++/WinRT 中实现可观测集合,以及如何将 XAML 项控件绑定到它们(有关背景信息,请参阅 数据绑定)。

如果要遵循本主题,建议先创建 XAML 控件中描述的项目;绑定到 C++/WinRT 属性。 本主题向该项目添加更多代码,并添加到该主题中介绍的概念。

Important

有关有助于你了解如何使用和编写运行时类(借助 C++/WinRT)的基本概念和术语,请参阅 借助 C++/WinRT 使用 API借助 C++/WinRT 编写 API

对于集合来说,可观察是什么意思?

如果表示集合的运行时类选择在向集合中添加或从中移除元素时引发 IObservableVector<T>::VectorChanged 事件,则运行时类是可观察的集合。 XAML 项控件可以通过检索更新的集合并更新自身以显示当前元素来绑定到和处理这些事件。

注释

有关安装和使用 C++/WinRT Visual Studio 扩展(VSIX)和 NuGet 包(一起提供项目模板和生成支持)的信息,请参阅Visual Studio对 C++/WinRT 的支持

BookSkus 集合添加到 BookstoreViewModel

XAML 控件;绑定到 C++/WinRT 属性一文中,我们向主视图模型添加了一个BookSku类型的属性。 在此步骤中,我们将使用 winrt::single_threaded_observable_vector 工厂函数模板来帮助我们在同一视图模型上实现 BookSku 的可观测集合。

BookstoreViewModel.idl 中声明新属性。

// BookstoreViewModel.idl
...
runtimeclass BookstoreViewModel
{
    BookSku BookSku{ get; };
    Windows.Foundation.Collections.IObservableVector<BookSku> BookSkus{ get; };
}
...

Important

使用 C++/WinRT 绑定到集合时,比使用 C# 要复杂一些。 在上面的 MIDL 3.0 列表中,请注意 BookSkus 属性的类型为 BookSkuIObservableVector。 在本主题的下一部分中,我们将将 ListBox 的项源绑定到 BookSkus。 列表框是项控件,若要正确设置 ItemsControl.ItemsSource 属性,需要将其设置为 IObservableVectorIVector 类型的值,或者 IBindableObservableVector 等互操作性类型。 否则, {x:Bind} 将生成 E_INVALIDARG,并且 {Binding} 会以无提示方式失败。

Warning

本主题中显示的代码适用于 C++/WinRT 版本 2.0.190530.8 或更高版本。 如果使用的是早期版本,则需要对显示的代码进行一些细微调整。 在上面的 MIDL 3.0 列表中,将 BookSkus 属性更改为 IInspectableIObservableVector。 然后在实现中使用 IInspectable (而不是 BookSku)。

保存并生成。 复制 \Bookstore\Bookstore\Generated Files\sources 文件夹中 BookstoreViewModel.hBookstoreViewModel.cpp 里的访问器存根(有关详细信息,请参阅上一主题,XAML 控件; 绑定到 C++/WinRT 属性)。 实现此类访问器存根。

// BookstoreViewModel.h
...
struct BookstoreViewModel : BookstoreViewModelT<BookstoreViewModel>
{
    BookstoreViewModel();

    Bookstore::BookSku BookSku();

    Windows::Foundation::Collections::IObservableVector<Bookstore::BookSku> BookSkus();

private:
    Bookstore::BookSku m_bookSku{ nullptr };
    Windows::Foundation::Collections::IObservableVector<Bookstore::BookSku> m_bookSkus;
};
...
// BookstoreViewModel.cpp
...
BookstoreViewModel::BookstoreViewModel()
{
    m_bookSku = winrt::make<Bookstore::implementation::BookSku>(L"Atticus");
    m_bookSkus = winrt::single_threaded_observable_vector<Bookstore::BookSku>();
    m_bookSkus.Append(m_bookSku);
}

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

Windows::Foundation::Collections::IObservableVector<Bookstore::BookSku> BookstoreViewModel::BookSkus()
{
    return m_bookSkus;
}
...

将 ListBox 绑定到 BookSkus 属性

打开 MainPage.xaml,其中包含主 UI 页面的 XAML 标记。 在与Button相同的StackPanel中添加以下标记。

<ListBox ItemsSource="{x:Bind MainViewModel.BookSkus}">
    <ItemsControl.ItemTemplate>
        <DataTemplate x:DataType="local:BookSku">
            <TextBlock Text="{x:Bind Title, Mode=OneWay}"/>
        </DataTemplate>
    </ItemsControl.ItemTemplate>
</ListBox>

MainPage.cpp 中,向 Click 事件处理程序添加一行代码,以将一本书追加到集合中。

// MainPage.cpp
...
void MainPage::ClickHandler(IInspectable const&, RoutedEventArgs const&)
{
    MainViewModel().BookSku().Title(L"To Kill a Mockingbird");
    MainViewModel().BookSkus().Append(winrt::make<Bookstore::implementation::BookSku>(L"Moby Dick"));
}
...

现在构建并运行项目。 单击该按钮以执行 Click 事件处理程序。 我们看到 Append 的实现引发了一个事件,让 UI 知道集合已更改;和 ListBox 重新查询集合以更新其自己的 Items 值。 就像以前一样,其中一本书的标题发生了变化:并且标题更改同时反映在按钮和列表框中。

重要 API