在內部,Windows 執行階段 集合包含許多複雜的運作部分。 但當你想把集合物件傳給 Windows 執行階段 函式,或實作自己的集合屬性和集合類型時,C++/WinRT 裡有函式和基底類別可以支援你。 這些功能可讓你免於處理複雜性,並為你省下大量時間與精力上的額外負擔。
IVector 是由任何隨機存取元素集合實作的 Windows 執行階段 介面。 如果你要自己實作 IVector ,還需要實作 IIterable、 IVectorView 和 IIterator。 即使 你需要 自訂的集合類型,那也是一項大量的工作。 但如果你的資料是 std::vector(或 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(假設你之後不會再存取它)移入函式。 在這兩種情況下,你都是把 rvalue 傳入函數。 這讓編譯器能更有效率,避免複製資料。 如果你想了解更多關於 r值的資訊,請參閱 「值類別」及其相關參考資料。
如果你想把 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 項目控制;但這個收藏是無法觀察到的。
可觀察集合
若要擷取實作 observable 集合之類型的新物件,請使用任意元素類型呼叫 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 傳回可觀察的關聯式集合。
你可以選擇性地將 rvalue 傳遞給函式 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)) };
單執行緒
這些函式名稱中的「單執行緒」表示它們不提供任何並行——換句話說,它們並非執行緒安全。 關於執行緒的提及與 apartments 無關,因為這些函式回傳的物件皆為敏捷(參見 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 struct 模板中衍生出自訂向量視圖會簡單得多,然後實作 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 結構範本