Auflistungen mit C++/WinRT

Im Inneren hat eine Windows-Runtime-Sammlung viele komplexe Bestandteile. Wenn Sie jedoch ein Auflistungsobjekt an eine Windows-Runtime-Funktion übergeben oder Eigene Sammlungseigenschaften und Sammlungstypen implementieren möchten, gibt es Funktionen und Basisklassen in C++/WinRT, die Sie unterstützen. Diese Funktionen nehmen Ihnen die Komplexität ab und sparen Ihnen viel Aufwand an Zeit und Mühe.

IVector ist die Windows-Runtime Schnittstelle, die von jeder Sammlung von Elementen mit wahlfreiem Zugriff implementiert wird. Wenn Sie IVector selbst implementieren möchten, müssen Sie auch IIterable, IVectorView und IIterator implementieren. Auch wenn Sie einen benutzerdefinierten Sammlungstyp benötigen , ist das viel Arbeit. Wenn Sie jedoch Daten in einem std::vector (oder in einer std::map oder in einer std::unordered_map) haben und diese nur an eine Windows-Runtime-API übergeben möchten, sollten Sie diesen Aufwand möglichst vermeiden. Und das lässt sich vermeiden, denn C++/WinRT hilft Ihnen, Sammlungen effizient und mit wenig Aufwand zu erstellen.

Siehe auch XAML-Elementsteuerelemente; Binden an eine C++/WinRT-Auflistung.

Hilfsfunktionen für Sammlungen

Allgemeine Sammlung, leer

In diesem Abschnitt wird das Szenario behandelt, in dem Sie eine zunächst leere Sammlung erstellen möchten. und füllen Sie sie dann nach der Erstellung auf.

Um ein neues Objekt eines Typs zu erhalten, der eine allgemeine Sammlung implementiert, können Sie die Funktionsvorlage winrt::single_threaded_vector aufrufen. Das Objekt wird als IVector zurückgegeben, und das ist die Schnittstelle, über die Sie die Funktionen und Eigenschaften des zurückgegebenen Objekts aufrufen.

Wenn Sie die folgenden Codebeispiele direkt in die Hauptquellcodedatei eines Windows Konsolenanwendungsprojekts (C++/WinRT) kopieren möchten, legen Sie zuerst "Not Using Precompiled Headers" in Projekteigenschaften fest.

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

Wie Sie im obigen Codebeispiel sehen können, können Sie nach dem Erstellen der Auflistung Elemente anfügen, sie durchlaufen und das Objekt in der Regel so behandeln, als würden Sie jedes Windows-Runtime-Auflistungsobjekts behandeln, das Sie möglicherweise von einer API erhalten haben. Wenn Sie eine unveränderliche Ansicht über die Sammlung benötigen, können Sie IVector::GetView aufrufen, wie gezeigt. Das oben gezeigte Muster – das Erstellen und Verwenden einer Sammlung – eignet sich für einfache Szenarien, in denen Sie Daten an eine API übergeben oder aus dieser herausholen möchten. Sie können einen IVector oder eine IVectorView überall dort übergeben, wo ein IIterable erwartet wird.

Im obigen Codebeispiel initialisiert der Aufruf von winrt::init_apartment den Thread in der Windows-Runtime; standardmäßig als Multithread-Apartment. Der Aufruf initialisiert auch COM.

Allgemeine Sammlung, auf Basis von Daten vorbereitet

In diesem Abschnitt wird das Szenario behandelt, in dem Sie eine Sammlung erstellen und gleichzeitig auffüllen möchten.

Sie können den Overhead der Aufrufe von Append im vorherigen Codebeispiel vermeiden. Möglicherweise verfügen Sie bereits über die Quelldaten, oder Sie möchten die Quelldaten vor dem Erstellen des Windows-Runtime-Auflistungsobjekts auffüllen. Hier erfahren Sie, wie Sie das machen können.

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

Sie können ein temporäres Objekt, das Ihre Daten enthält, wie oben mit coll1 an winrt::single_threaded_vector übergeben. Sie können auch einen std::vector in die Funktion verschieben (vorausgesetzt, Sie greifen danach nicht mehr darauf zu). In beiden Fällen übergeben Sie einen Wert an die Funktion. Dies ermöglicht es dem Compiler, effizient zu sein und das Kopieren der Daten zu vermeiden. Weitere Informationen zu Wertwerten finden Sie unter Wertkategorien und Verweise darauf.

Wenn Sie ein XAML-ItemsControl an Ihre Sammlung binden möchten, dann können Sie das. Beachten Sie jedoch, dass Sie zum ordnungsgemäßen Festlegen der ItemsControl.ItemsSource-Eigenschaft den Wert des Typs "IVector " von "IInspectable " (oder eines Interoperabilitätstyps wie "IBindableObservableVector") festlegen müssen.

Hier ist ein Codebeispiel, das eine Sammlung eines zum Binden geeigneten Typs erzeugt und ein Element an sie anhängt. Sie finden den Kontext für dieses Codebeispiel in XAML-Elementsteuerelementen; binden sie an eine C++/WinRT-Auflistung.

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

Sie können eine Windows-Runtime Sammlung aus Daten erstellen und eine Ansicht darauf abrufen, die an eine API übergeben werden kann, ohne etwas zu kopieren.

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

In den obigen Beispielen kann die von uns erstellte Sammlung an ein XAML-ItemsControl gebunden werden; aber die Sammlung ist nicht beobachtbar.

Beobachtbare Sammlung

Um ein neues Objekt eines Typs abzurufen, der eine beobachtbare Sammlung implementiert, rufen Sie die Funktionsvorlage winrt::single_threaded_observable_vector für einen beliebigen Elementtyp auf. Verwenden Sie IInspectable jedoch als Elementtyp, um eine feststellbare Auflistung für die Bindung an ein XAML-Elementsteuerelement zu erstellen.

Das Objekt wird als IObservableVector zurückgegeben, und das ist die Schnittstelle, über die Sie (oder das Steuerelement, an das es gebunden ist) die Funktionen und Eigenschaften des zurückgegebenen Objekts aufrufen.

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

Weitere Details und Codebeispiele zum Binden von Ui-Steuerelementen an eine feststellbare Auflistung finden Sie unter XAML-Elementsteuerelemente; Binden an eine C++/WinRT-Auflistung.

Associative-Auflistung (Karte)

Es gibt assoziative Sammlungsversionen der beiden Funktionen, die wir betrachtet haben.

Sie können diese Auflistungen optional mit Daten primieren, indem Sie an die Funktion einen Wert vom Typ "std::map " oder "std::unordered_map" übergeben.

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

Mit nur einem Thread

Der "Singlethreaded" in den Namen dieser Funktionen gibt an, dass sie keine Parallelität bieten , d. h. sie sind nicht threadsicher. Die Erwähnung von Threads ist nicht mit Wohnungen verknüpft, da die von diesen Funktionen zurückgegebenen Objekte agil sind (siehe Agile-Objekte in C++/WinRT). Es ist nur so, dass die Objekte single-threaded sind. Und das ist völlig angemessen, wenn Sie nur Daten auf eine Oder andere Weise über die Binäre Schnittstelle (Application Binary Interface, ABI) übergeben möchten.

Basisklassen für Sammlungen

Wenn Sie ihre eigene benutzerdefinierte Sammlung vollständig flexibel implementieren möchten, sollten Sie dies vermeiden. So würde beispielsweise eine benutzerdefinierte Vektoransicht ohne Unterstützung der Basisklassen von C++/WinRT aussehen.

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

Stattdessen ist es viel einfacher, Die benutzerdefinierte Vektoransicht von der Winrt::vector_view_base Strukturvorlage abzuleiten und einfach die get_container-Funktion zu implementieren, um den Container verfügbar zu machen, der Ihre Daten enthält.

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

Der von get_container zurückgegebene Container muss die Anfangs - und Endschnittstelle bereitstellen, die winrt::vector_view_base erwartet. Wie im obigen Beispiel gezeigt, stellt "std::vector " dies bereit. Sie können jedoch jeden Container zurückgeben, der denselben Vertrag erfüllt, einschließlich Ihres eigenen benutzerdefinierten Containers.

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

Dies sind die Basisklassen, die C++/WinRT bereitstellt, um benutzerdefinierte Auflistungen zu implementieren.

winrt::vector_view_base

Sehen Sie sich die obigen Codebeispiele an.

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

Wichtige APIs