在内部实现上,Windows 运行时 集合由许多复杂的组成部分构成。 但是,如果要将集合对象传递给Windows 运行时函数,或实现自己的集合属性和集合类型,则 C++/WinRT 中有一些函数和基类来支持你。 这些功能可帮你摆脱复杂性,并为你节省大量时间、精力和额外成本。
IVector 是由任何随机访问元素集合实现的Windows 运行时接口。 如果你要自己实现 IVector ,则还需要实现 IIterable、 IVectorView 和 IIterator。 即使你需要自定义集合类型,那也是一项很大的工作。 但是,如果在 std::vector(或 std::map 或 std::map 或 std::unordered_map)中拥有数据,并且你只想将该数据传递给 Windows 运行时 API,则最好避免执行该级别的工作(如果可能)。 而且,避免这种情况是可能的,因为 C++/WinRT 可帮助你高效地创建集合,并且只需很少的工作量。
另请参阅 XAML 项控件;绑定到 C++/WinRT 集合。
用于集合的辅助函数
常规用途集合,空
本部分介绍要创建最初为空的集合的方案;然后在创建 后 填充它。
若要检索实现常规用途集合的类型的新对象,可以调用 winrt::single_threaded_vector 函数模板。 该对象作为 IVector 返回,这是调用返回对象的函数和属性的接口。
如果要将以下代码示例直接复制粘贴到Windows控制台应用程序(C++/WinRT)项目的主源代码文件中,请先在项目属性中设置“不使用预编译标头”。
// main.cpp
#include <winrt/Windows.Foundation.Collections.h>
#include <iostream>
using namespace winrt;
int main()
{
winrt::init_apartment();
Windows::Foundation::Collections::IVector<int> coll{ winrt::single_threaded_vector<int>() };
coll.Append(1);
coll.Append(2);
coll.Append(3);
for (auto const& el : coll)
{
std::cout << el << std::endl;
}
Windows::Foundation::Collections::IVectorView<int> view{ coll.GetView() };
}
如上面的代码示例所示,创建集合后,可以追加元素、循环访问这些元素,并将对象视为你可能从 API 接收的任何Windows 运行时集合对象。 如果需要集合上的不可变视图,则可以调用 IVector::GetView,如下所示。 上面所示的模式(用于创建和使用集合)适用于要将数据传入或从 API 中获取数据的简单方案。 可以在 IIterable 预期的任何位置传递 IVector 或 IVectorView。
在上面的代码示例中,对 winrt::init_apartment 的调用在多线程单元中默认初始化 Windows 运行时; 中的线程。 该调用还会初始化 COM。
通用集合,由数据预先生成
本部分介绍要创建集合并同时填充该集合的方案。
可以避免前面的代码示例中对 Append 的调用开销。 你可能已经有源数据,或者你可能更喜欢在创建Windows 运行时集合对象之前填充源数据。 操作方式如下。
auto coll1{ winrt::single_threaded_vector<int>({ 1,2,3 }) };
std::vector<int> values{ 1,2,3 };
auto coll2{ winrt::single_threaded_vector<int>(std::move(values)) };
for (auto const& el : coll2)
{
std::cout << el << std::endl;
}
可以将包含数据的临时对象传递给 winrt::single_threaded_vector,如 coll1上所示。 或者,可以将 std::vector (假设你不会再次访问它)移动到函数中。 在这两种情况下,将 右值 传递到函数中。 这使编译器能够高效并避免复制数据。 如果想要详细了解 右值,请参阅 值类别及其引用。
如果要将 XAML 项控件绑定到集合,则可以。 但请注意,若要正确设置 ItemsControl.ItemsSource 属性,需要将其设置为 IInspectable 类型 IVector 的值(或 IBindableObservableVector 等互操作性类型)。
下面是一个代码示例,它生成适合绑定的类型集合,并将元素追加到其中。 可以在 XAML 项控件中找到此代码示例的上下文 ;绑定到 C++/WinRT 集合。
auto bookSkus{ winrt::single_threaded_vector<Windows::Foundation::IInspectable>() };
bookSkus.Append(winrt::make<Bookstore::implementation::BookSku>(L"Moby Dick"));
可以从数据创建一个Windows 运行时集合,并获取一个视图,以便将其传递给 API,无需复制任何内容。
std::vector<float> values{ 0.1f, 0.2f, 0.3f };
Windows::Foundation::Collections::IVectorView<float> view{ winrt::single_threaded_vector(std::move(values)).GetView() };
在上面的示例中,我们创建的集合 可以 绑定到 XAML 项控件;但集合不可观测。
可观测集合
若要检索实现 可观察 集合的类型的新对象,请使用任何元素类型调用 winrt::single_threaded_observable_vector 函数模板。 但是,若要使可观测集合适合绑定到 XAML 项控件,请使用 IInspectable 作为元素类型。
该对象以 IObservableVector 形式返回,你(或与其绑定的控件)正是通过该接口调用该返回对象的方法和属性。
auto bookSkus{ winrt::single_threaded_observable_vector<Windows::Foundation::IInspectable>() };
有关将用户界面(UI)控件绑定到可观察集合的更多详细信息和代码示例,请参阅 XAML 项控件;绑定到 C++/WinRT 集合。
关联集合 (地图)
我们已经看过的那两个函数都有对应的关联集合版本。
- winrt::single_threaded_map 函数模板将不可观测的关联集合作为 IMap 返回。
- winrt::single_threaded_observable_map 函数模板将可观测关联集合作为 IObservableMap 返回。
你可以选择向该函数传递一个类型为std::map或std::unordered_map的右值,以预先用数据填充这些集合。
auto coll1{
winrt::single_threaded_map<winrt::hstring, int>(std::map<winrt::hstring, int>{
{ L"AliceBlue", 0xfff0f8ff }, { L"AntiqueWhite", 0xfffaebd7 }
})
};
std::map<winrt::hstring, int> values{
{ L"AliceBlue", 0xfff0f8ff }, { L"AntiqueWhite", 0xfffaebd7 }
};
auto coll2{ winrt::single_threaded_map<winrt::hstring, int>(std::move(values)) };
单线程
这些函数名称中的“单线程”表示它们不提供任何并发性,换句话说,它们不是线程安全的。 线程的提及与单元无关,因为从这些函数返回的对象都是敏捷的(请参阅 C++/WinRT 中的敏捷对象)。 只是对象是单线程的。 如果只想通过应用程序二进制接口(ABI)以一种方式或其他方式传递数据,这完全合适。
集合的基类
如果想要实现自己的自定义集合,若要实现完全的灵活性,则需要避免以硬性方式执行此操作。 例如,如果没有 C++/WinRT 基类的协助,自定义向量视图的外观就是这样。
...
using namespace winrt;
using namespace Windows::Foundation::Collections;
...
struct MyVectorView :
implements<MyVectorView, IVectorView<float>, IIterable<float>>
{
// IVectorView
float GetAt(uint32_t const) { ... };
uint32_t GetMany(uint32_t, winrt::array_view<float>) const { ... };
bool IndexOf(float, uint32_t&) { ... };
uint32_t Size() { ... };
// IIterable
IIterator<float> First() const { ... };
};
...
IVectorView<float> view{ winrt::make<MyVectorView>() };
相反,从 winrt::vector_view_base 结构模板派生自定义矢量视图更容易,只需实现 get_container 函数即可公开保存数据的容器。
struct MyVectorView2 :
implements<MyVectorView2, IVectorView<float>, IIterable<float>>,
winrt::vector_view_base<MyVectorView2, float>
{
auto& get_container() const noexcept
{
return m_values;
}
private:
std::vector<float> m_values{ 0.1f, 0.2f, 0.3f };
};
get_container返回的容器必须提供 winrt::vector_view_base预期的开始和结束接口。 如上面的示例所示, std::vector 提供这一点。 但是,可以返回任何满足同一协定的容器,包括你自己的自定义容器。
struct MyVectorView3 :
implements<MyVectorView3, IVectorView<float>, IIterable<float>>,
winrt::vector_view_base<MyVectorView3, float>
{
auto get_container() const noexcept
{
struct container
{
float const* const first;
float const* const last;
auto begin() const noexcept
{
return first;
}
auto end() const noexcept
{
return last;
}
};
return container{ m_values.data(), m_values.data() + m_values.size() };
}
private:
std::array<float, 3> m_values{ 0.2f, 0.3f, 0.4f };
};
这些是 C++/WinRT 提供的基类,可帮助你实现自定义集合。
winrt::vector_view_base
请参阅上面的代码示例。
winrt::vector_base
struct MyVector :
implements<MyVector, IVector<float>, IVectorView<float>, IIterable<float>>,
winrt::vector_base<MyVector, float>
{
auto& get_container() const noexcept
{
return m_values;
}
auto& get_container() noexcept
{
return m_values;
}
private:
std::vector<float> m_values{ 0.1f, 0.2f, 0.3f };
};
winrt::observable_vector_base
struct MyObservableVector :
implements<MyObservableVector, IObservableVector<float>, IVector<float>, IVectorView<float>, IIterable<float>>,
winrt::observable_vector_base<MyObservableVector, float>
{
auto& get_container() const noexcept
{
return m_values;
}
auto& get_container() noexcept
{
return m_values;
}
private:
std::vector<float> m_values{ 0.1f, 0.2f, 0.3f };
};
winrt::map_view_base
struct MyMapView :
implements<MyMapView, IMapView<winrt::hstring, int>, IIterable<IKeyValuePair<winrt::hstring, int>>>,
winrt::map_view_base<MyMapView, winrt::hstring, int>
{
auto& get_container() const noexcept
{
return m_values;
}
private:
std::map<winrt::hstring, int> m_values{
{ L"AliceBlue", 0xfff0f8ff }, { L"AntiqueWhite", 0xfffaebd7 }
};
};
winrt::map_base
struct MyMap :
implements<MyMap, IMap<winrt::hstring, int>, IMapView<winrt::hstring, int>, IIterable<IKeyValuePair<winrt::hstring, int>>>,
winrt::map_base<MyMap, winrt::hstring, int>
{
auto& get_container() const noexcept
{
return m_values;
}
auto& get_container() noexcept
{
return m_values;
}
private:
std::map<winrt::hstring, int> m_values{
{ L"AliceBlue", 0xfff0f8ff }, { L"AntiqueWhite", 0xfffaebd7 }
};
};
winrt::observable_map_base
struct MyObservableMap :
implements<MyObservableMap, IObservableMap<winrt::hstring, int>, IMap<winrt::hstring, int>, IMapView<winrt::hstring, int>, IIterable<IKeyValuePair<winrt::hstring, int>>>,
winrt::observable_map_base<MyObservableMap, winrt::hstring, int>
{
auto& get_container() const noexcept
{
return m_values;
}
auto& get_container() noexcept
{
return m_values;
}
private:
std::map<winrt::hstring, int> m_values{
{ L"AliceBlue", 0xfff0f8ff }, { L"AntiqueWhite", 0xfffaebd7 }
};
};
重要 API
- ItemsControl.ItemsSource 属性
- IObservableVector 接口
- IVector 接口
- winrt::map_base 结构模板
- winrt::map_view_base 结构模板
- winrt::observable_map_base结构模板
- winrt::observable_vector_base 结构模板
- winrt::single_threaded_observable_map 函数模板
- winrt::single_threaded_map 函数模板
- winrt::single_threaded_observable_vector 函数模板
- winrt::single_threaded_vector 函数模板
- winrt::vector_base 结构模板
- winrt::vector_view_base 结构模板