Kolekce s C++/WinRT

Interně má kolekce prostředí Windows Runtime spoustu složitých pohyblivých částí. Pokud ale chcete předat objekt kolekce funkci prostředí Windows Runtime nebo implementovat vlastní vlastnosti kolekce a typy kolekcí, existují funkce a základní třídy v jazyce C++/WinRT, které vás podporují. Tyto funkce vás zbaví složitosti a ušetří vám spoustu času i námahy.

IVector je prostředí Windows Runtime rozhraní implementované libovolnou kolekcí prvků s náhodným přístupem. Pokud byste implementovali IVector sami, budete také muset implementovat IIterable, IVectorView a IIterator. I když potřebujete vlastní typ kolekce, je to hodně práce. Pokud ale máte data v souboru std::vector (nebo std::map nebo std::unordered_map) a vše, co chcete udělat, je předat do prostředí Windows Runtime rozhraní API, pak byste se chtěli vyhnout této úrovni práce, pokud je to možné. A vyhnout se tomu je možné, protože C++/WinRT pomáhá efektivně a s minimálním úsilím vytvářet kolekce.

Viz také ovládací prvky položek XAML, vazba na kolekci C++/WinRT.

Pomocné funkce pro kolekce

Kolekce pro obecné účely, prázdná

Tato část popisuje scénář, ve kterém chcete vytvořit kolekci, která je původně prázdná. a potom ho po vytvoření naplňte.

Chcete-li získat nový objekt typu, který implementuje kolekci obecného účelu, můžete zavolat šablonu funkce winrt::single_threaded_vector. Objekt je vrácen jako IVector a to je rozhraní, přes které voláte vrácené funkce a vlastnosti objektu.

Pokud chcete zkopírovat následující příklady kódu přímo do hlavního zdrojového souboru projektu konzolové aplikace Windows (C++/WinRT), pak nejprve ve vlastnostech projektu nastavte možnost Nepoužít předkompilované hlavičky.

// 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() };
}

Jak vidíte v příkladu kódu výše, po vytvoření kolekce můžete přidávat prvky, iterovat nad nimi a obecně považovat objekt za jakýkoli prostředí Windows Runtime objekt kolekce, který jste mohli získat z rozhraní API. Pokud potřebujete neměnné zobrazení v kolekci, můžete volat IVector::GetView, jak je znázorněno. Vzor uvedený výše – vytvoření a využívání kolekce – je vhodný pro jednoduché scénáře, ve kterých chcete předávat data nebo z tohoto rozhraní API získat data. Můžete předat IVector nebo IVectorView všude tam, kde se očekává IIterable.

Ve výše uvedeném příkladu kódu volání winrt::init_apartment inicializuje vlákno v prostředí prostředí Windows Runtime; ve výchozím nastavení ve vícevláknovém apartmá. Volání také inicializuje COM.

Univerzální kolekce, inicializovaná z dat

Tato část popisuje scénář, ve kterém chcete vytvořit kolekci a naplnit ji současně.

Můžete se vyhnout režii volání metody Append v předchozím příkladu kódu. Možná už máte zdrojová data nebo chcete zdrojová data naplnit předem vytvořením objektu kolekce prostředí Windows Runtime. Tady je postup, jak to udělat.

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;
}

Dočasný objekt obsahující vaše data můžete předat do winrt::single_threaded_vector, jako v případě coll1 výše. Nebo můžete přesunout objekt std::vector (za předpokladu, že k funkci nebudete znovu přistupovat). V obou případech předáváte do funkce rvalue. To umožňuje kompilátoru být efektivní a vyhnout se kopírování dat. Pokud se chcete dozvědět více o hodnotách rvalue, přečtěte si téma Kategorie hodnot a odkazy na ně.

Pokud chcete vytvořit vazbu ovládacího prvku položek XAML s kolekcí, můžete to udělat. Mějte ale na paměti, že chcete-li vlastnost ItemsControl.ItemsSource nastavit správně, musíte ji nastavit na hodnotu typu IVector objektů typu IInspectable (nebo typu pro interoperabilitu, například IBindableObservableVector).

Tady je příklad kódu, který vytvoří kolekci typu vhodného pro vazbu a připojí k němu prvek. Kontext tohoto příkladu kódu najdete v ovládacích prvcích položek XAML; svázat s kolekcí C++/WinRT.

auto bookSkus{ winrt::single_threaded_vector<Windows::Foundation::IInspectable>() };
bookSkus.Append(winrt::make<Bookstore::implementation::BookSku>(L"Moby Dick"));

Můžete vytvořit kolekci prostředí Windows Runtime z dat a získat její zobrazení připravené k předání do rozhraní API, a to vše bez nutnosti kopírování čehokoli.

std::vector<float> values{ 0.1f, 0.2f, 0.3f };
Windows::Foundation::Collections::IVectorView<float> view{ winrt::single_threaded_vector(std::move(values)).GetView() };

V příkladech výše může být kolekce, která vytvoříme, vázána na ovládací prvek položek XAML; ale kolekce není pozorovatelná.

Pozorovatelná kolekce

Pokud chcete načíst nový objekt typu, který implementuje pozorovatelnou kolekci, zavolejte šablonu funkce winrt::single_threaded_observable_vector s libovolným typem elementu. Chcete-li však vytvořit pozorovatelnou kolekci, která je vhodná pro vazbu na ovládací prvek položek XAML, použijte jako typ elementu IInspectable .

Objekt se vrací jako IObservableVector a toto je rozhraní, prostřednictvím kterého vy (nebo ovládací prvek, k němuž je objekt navázán) přistupujete k funkcím a vlastnostem vráceného objektu.

auto bookSkus{ winrt::single_threaded_observable_vector<Windows::Foundation::IInspectable>() };

Další podrobnosti a příklady kódu týkající se vazby ovládacích prvků uživatelského rozhraní k pozorovatelné kolekci najdete v tématu Ovládací prvky položek XAML; svázání s kolekcí C++/WinRT.

Asociativní kolekce (mapa)

Existují asociativní verze kolekcí dvou funkcí, na které jsme se podívali.

Tyto kolekce můžete volitelně naplnit daty předáním funkci rvalue typu std::map nebo 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)) };

Jednovláknové

"Jednovláknové" v názvech těchto funkcí označuje, že neposkytují žádnou souběžnost – jinými slovy, nejsou bezpečné pro přístup z více vláken. Zmínka o vláknech nesouvisí s apartmány, protože objekty vrácené z těchto funkcí jsou všechny agilní (viz Agilní objekty v C++/WinRT). Je to jen to, že objekty jsou jednovláknové. A to je zcela vhodné, pokud chcete pouze předávat data jedním nebo druhým přes binární rozhraní aplikace (ABI).

Základní třídy pro kolekce

Pokud chcete mít naprostou flexibilitu a implementovat vlastní kolekci, budete se chtít vyhnout tomu dělat to složitou cestou. Například to, jak by vypadalo vlastní vektorové zobrazení bez pomoci základních tříd 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>() };

Místo toho je mnohem jednodušší odvodit vlastní vektorové zobrazení ze šablony struktury winrt::vector_view_base a stačí implementovat funkci get_container pro zveřejnění kontejneru, který obsahuje vaše data.

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 };
};

Kontejner vrácený get_container musí poskytnout počáteční a koncové rozhraní, které winrt::vector_view_base očekává. Jak je znázorněno v příkladu výše, příkaz std::vector to poskytuje. Můžete ale vrátit libovolný kontejner, který splňuje stejnou smlouvu, včetně vlastního kontejneru.

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 };
};

Jedná se o základní třídy, které jazyk C++/WinRT poskytuje, aby vám pomohly implementovat vlastní kolekce.

winrt::vector_view_base

Podívejte se na výše uvedené příklady kódu.

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 }
    };
};

Důležitá rozhraní API