使用 C++/WinRT 的集合
就內部而言,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 或從 API 取得資料的簡單案例。 如果預期 IIterable,則可傳遞 IVector 或 IVectorView。
在以上的程式碼範例中,對 winrt::init_apartment 的呼叫會初始化 Windows 執行階段中的執行緒;預設是在多執行緒 Apartment 中。 此呼叫也會初始化 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 傳遞到函式。 這可提升編譯器的效率,並避免複製資料。 如果您想要深入了解 rvalue,請參閱值類別和它們的參考一文。
如果想要將 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 的 rvalue,使用資料準備這些集合。
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)) };
單一執行緒
這些函式的名稱中出現的「單一執行緒」指出函式不提供任何並行,也就是說,並非執行緒安全。 提及的執行緒與 Apartment 無關,因為從這些函式傳回的物件都是敏捷式 (請參閱 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 預期的 begin 和 end 介面。 如以上範例所示,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 結構範本