Delen via


Gebeurtenissen verwerken met behulp van gemachtigden in C++/WinRT

In dit onderwerp wordt beschreven hoe u gedelegeerden voor gebeurtenisafhandeling registreert en intrekt met behulp van C++/WinRT-. U kunt een gebeurtenis afhandelen met elk standaard C++-functieachtig object.

Opmerking

Zie voor informatie over het installeren en gebruiken van de C++/WinRT Visual Studio-extensie (VSIX) en het NuGet-pakket (dat samen projectsjabloon en build-ondersteuning biedt), Visual Studio-ondersteuning voor C++/WinRT-.

Visual Studio gebruiken om een gebeurtenis-handler toe te voegen

Een handige manier om een gebeurtenishandler aan uw project toe te voegen, is met behulp van de XAML Designer-gebruikersinterface (UI) in Visual Studio. Wanneer de XAML-pagina is geopend in de XAML Designer, selecteert u het besturingselement waarvan u de gebeurtenis wilt verwerken. Klik op de eigenschappenpagina van dat besturingselement op het bliksemschichtpictogram om alle gebeurtenissen weer te geven die afkomstig zijn van dat besturingselement. Dubbelklik vervolgens op de gebeurtenis die u wilt verwerken; bijvoorbeeld OnClicked.

De XAML Designer voegt het juiste prototype van de gebeurtenishandlerfunctie (en een stub-implementatie) toe aan uw bronbestanden, zodat u deze kunt vervangen door uw eigen implementatie.

Opmerking

Normaal gesproken hoeven uw gebeurtenis-handlers niet te worden beschreven in uw Midl-bestand (.idl). De XAML Designer voegt dus geen prototypen van de gebeurtenishandlerfunctie toe aan uw Midl-bestand. Hiermee worden alleen uw bestanden .h en .cpp toegevoegd.

Een gemachtigde registreren om een gebeurtenis af te handelen

Een eenvoudig voorbeeld is het verwerken van een klikgebeurtenis van een knop. Het is gebruikelijk dat u XAML-markeringen gebruikt om een lidfunctie te registreren voor het afhandelen van de gebeurtenis, zoals deze.

// MainPage.xaml
<Button x:Name="myButton" Click="ClickHandler">Click Me</Button>
// MainPage.h
void ClickHandler(
    winrt::Windows::Foundation::IInspectable const& sender,
    winrt::Windows::UI::Xaml::RoutedEventArgs const& args);

// MainPage.cpp
void MainPage::ClickHandler(
    IInspectable const& /* sender */,
    RoutedEventArgs const& /* args */)
{
    myButton().Content(box_value(L"Clicked"));
}

De bovenstaande code is afkomstig uit het Blanco App (C++/WinRT)-project in Visual Studio. De code roept een gegenereerde accessorfunctie aan, die de Button retourneert, die we myButtonhebben genoemd. Als u de x:Name van dat knop element wijzigt, wordt ook de naam van de gegenereerde accessorfunctie gewijzigd.

Opmerking

In dit geval is de gebeurtenisbron (het object dat de gebeurtenis genereert) de Knop met de naam myButton. En de ontvanger van de gebeurtenis (het object dat de gebeurtenis verwerkt) is een instantie van MainPage. Verderop in dit onderwerp vindt u meer informatie over het beheren van de levensduur van gebeurtenisbronnen en ontvangers van gebeurtenissen.

In plaats van dit declaratief in opmaak te doen, kunt u een methode imperatief registreren om een gebeurtenis af te handelen. Het is mogelijk niet duidelijk in het onderstaande codevoorbeeld, maar het argument voor de ButtonBase::Klik op aanroep is een exemplaar van de RoutedEventHandler gemachtigde. In dit geval gebruiken we de RoutedEventHandler constructoroverload die een object en een pointer-naar-lid-functie neemt.

// MainPage.cpp
MainPage::MainPage()
{
    InitializeComponent();

    myButton().Click({ this, &MainPage::ClickHandler });
}

Belangrijk

Bij het registreren van de gemachtigde geeft het bovenstaande codevoorbeeld een onbewerkte deze aanwijzer door (die verwijst naar het huidige object). Zie voor informatie over hoe een sterke of zwakke verwijzing naar het huidige object te maken. Als u een lidfunctie als gedelegeerde gebruikt.

Hier volgt een voorbeeld waarin een statische lidfunctie wordt gebruikt. noteer de eenvoudigere syntaxis.

// MainPage.h
static void ClickHandler(
    winrt::Windows::Foundation::IInspectable const& sender,
    winrt::Windows::UI::Xaml::RoutedEventArgs const& args);

// MainPage.cpp
MainPage::MainPage()
{
    InitializeComponent();

    myButton().Click( MainPage::ClickHandler );
}
void MainPage::ClickHandler(
    IInspectable const& /* sender */,
    RoutedEventArgs const& /* args */) { ... }

Er zijn andere manieren om een RoutedEventHandlerte maken. Hieronder ziet u het syntaxisblok uit het documentatieonderwerp voor RoutedEventHandler- (kies C++/WinRT- in de vervolgkeuzelijst Language in de rechterbovenhoek van de webpagina). Let op de verschillende constructors: de ene gebruikt een lambda; een andere een vrije functie; en nog een andere variant (de hierboven gebruikte versie) gebruikt een object en een pointer naar lidfunctie.

struct RoutedEventHandler : winrt::Windows::Foundation::IUnknown
{
    RoutedEventHandler(std::nullptr_t = nullptr) noexcept;
    template <typename L> RoutedEventHandler(L lambda);
    template <typename F> RoutedEventHandler(F* function);
    template <typename O, typename M> RoutedEventHandler(O* object, M method);
    /* ... other constructors ... */
    void operator()(winrt::Windows::Foundation::IInspectable const& sender,
        winrt::Windows::UI::Xaml::RoutedEventArgs const& e) const;
};

De syntaxis van de operator voor functie-aanroep is ook handig om te zien. Er wordt aangegeven wat de parameters van uw gedelegeerde moeten zijn. Zoals u kunt zien, komt de syntaxis van de functieoproepoperator overeen met de parameters van de MainPage::ClickHandler.

Opmerking

Als u voor een bepaalde gebeurtenis de details van de gemachtigde wilt achterhalen en de parameters van die gemachtigde, gaat u eerst naar het documentatieonderwerp voor de gebeurtenis zelf. Laten we de gebeurtenis UIElement.KeyDown als voorbeeld nemen. Ga naar dat onderwerp en kies C++/WinRT- in de vervolgkeuzelijst Language. In het syntaxisblok aan het begin van het onderwerp ziet u dit.

// Register
event_token KeyDown(KeyEventHandler const& handler) const;

Deze informatie vertelt ons dat de UIElement.KeyDown gebeurtenis (het onderwerp waar we het over hebben) het gedelegeerdetype KeyEventHandlerheeft, omdat dat het type is dat u doorgeeft bij het registreren van een gedelegeerde bij dit gebeurtenistype. Volg nu de koppeling over het onderwerp naar dat KeyEventHandler delegate type. Hier bevat het syntaxisblok een operator voor functie-aanroep. En, zoals hierboven vermeld, zegt het u wat de parameters van uw gedelegeerde moeten zijn.

void operator()(
  winrt::Windows::Foundation::IInspectable const& sender,
  winrt::Windows::UI::Xaml::Input::KeyRoutedEventArgs const& e) const;

Zoals u kunt zien, moet de gemachtigde worden gedeclareerd om een IInspectable als afzender te nemen en een instantie van de klasse KeyRoutedEventArgs als de argumenten.

Om een ander voorbeeld te nemen, laten we kijken naar de Popup.Closed-gebeurtenis. Het type afgevaardigde is EventHandler<IInspectable>. Uw gedelegeerde neemt dus een IInspectable als afzender en een andere IInspectable (omdat dat de parameter EventHandlertype is) als argument.

Als u niet veel werkt in uw gebeurtenishandler, kunt u een lambda-functie gebruiken in plaats van een lidfunctie. Nogmaals, het is mogelijk niet duidelijk uit het onderstaande codevoorbeeld, maar een RoutedEventHandler gemachtigde wordt samengesteld vanuit een lambda-functie die opnieuw moet overeenkomen met de syntaxis van de operator voor functieaanroep die we hierboven hebben besproken.

MainPage::MainPage()
{
    InitializeComponent();

    myButton().Click([this](IInspectable const& /* sender */, RoutedEventArgs const& /* args */)
    {
        myButton().Content(box_value(L"Clicked"));
    });
}

U kunt ervoor kiezen om iets explicieter te zijn wanneer u uw gemachtigde maakt. Als u deze bijvoorbeeld wilt doorgeven of meerdere keren wilt gebruiken.

MainPage::MainPage()
{
    InitializeComponent();

    auto click_handler = [](IInspectable const& sender, RoutedEventArgs const& /* args */)
    {
        sender.as<winrt::Windows::UI::Xaml::Controls::Button>().Content(box_value(L"Clicked"));
    };
    myButton().Click(click_handler);
    AnotherButton().Click(click_handler);
}

Een geregistreerde gemachtigde intrekken

Wanneer u een gemachtigde registreert, wordt doorgaans een token aan u geretourneerd. U kunt dat token vervolgens gebruiken om uw gemachtigde in te trekken; betekent dat de gemachtigde niet is geregistreerd bij de gebeurtenis en niet wordt aangeroepen als de gebeurtenis opnieuw wordt gegenereerd.

Om het eenvoudig te houden, liet geen van de bovenstaande codevoorbeelden zien hoe u dit kunt doen. In dit volgende codevoorbeeld wordt het token opgeslagen in de private datalid van de struct en wordt de handler in de destructor herroepen.

struct Example : ExampleT<Example>
{
    Example(winrt::Windows::UI::Xaml::Controls::Button const& button) : m_button(button)
    {
        m_token = m_button.Click([this](IInspectable const&, RoutedEventArgs const&)
        {
            // ...
        });
    }
    ~Example()
    {
        m_button.Click(m_token);
    }

private:
    winrt::Windows::UI::Xaml::Controls::Button m_button;
    winrt::event_token m_token;
};

In plaats van een sterke verwijzing, zoals in het bovenstaande voorbeeld, kunt u een zwakke verwijzing naar de knop opslaan (zie Sterke en zwakke verwijzingen in C++/WinRT).

Opmerking

Wanneer een gebeurtenisbron zijn gebeurtenissen synchroon genereert, kunt u de handler verwijderen en er zeker van zijn dat u geen verdere gebeurtenissen ontvangt. Maar voor asynchrone gebeurtenissen, zelfs na het intrekken (en vooral bij het intrekken binnen de destructor), kan een vluchtgebeurtenis uw object bereiken nadat het is begonnen met het vernietigen. Als u een plaats vindt om u af te melden voordat het wordt vernietigd, kan dit probleem worden beperkt of voor een robuuste oplossing ziet u Veilig toegang krijgen tot de deze aanwijzer met een gemachtigde voor het afhandelen van gebeurtenissen.

Als alternatief, wanneer u een gemachtigde registreert, kunt u winrt::auto_revoke (een waarde van het type winrt::auto_revoke_t) opgeven om een event-revoker aan te vragen (van het type winrt::event_revoker). De evenementenannuleerder houdt een zwakke verwijzing naar de gebeurtenisbron (het object dat het evenement veroorzaakt) voor u. U kunt deze handmatig intrekken door de functie event_revoker::revoke lid aan te roepen; maar de gebeurtenisintrekker roept die functie automatisch aan wanneer het buiten bereik raakt. De functie intrekt, controleert of de gebeurtenisbron nog bestaat en als dat het geval is, wordt uw gemachtigde ingetrokken. In dit voorbeeld hoeft u de gebeurtenisbron niet op te slaan en hoeft u geen destructor op te slaan.

struct Example : ExampleT<Example>
{
    Example(winrt::Windows::UI::Xaml::Controls::Button button)
    {
        m_event_revoker = button.Click(
            winrt::auto_revoke,
            [this](IInspectable const& /* sender */,
            RoutedEventArgs const& /* args */)
        {
            // ...
        });
    }

private:
    winrt::Windows::UI::Xaml::Controls::Button::Click_revoker m_event_revoker;
};

Hieronder ziet u het syntaxisblok uit het documentatieonderwerp voor de ButtonBase::Klik op gebeurtenis. Hierin worden de drie verschillende registratie- en intrekkingsfuncties weergegeven. U kunt precies zien welk type gebeurtenis-intrekkingsroutine u moet declareren vanaf de derde overbelasting. U kunt dezelfde soorten gemachtigden doorgeven aan zowel de register als de intrekken met event_revoker overloads.

// Register
winrt::event_token Click(winrt::Windows::UI::Xaml::RoutedEventHandler const& handler) const;

// Revoke with event_token
void Click(winrt::event_token const& token) const;

// Revoke with event_revoker
Button::Click_revoker Click(winrt::auto_revoke_t,
    winrt::Windows::UI::Xaml::RoutedEventHandler const& handler) const;

Opmerking

In het bovenstaande codevoorbeeld is Button::Click_revoker een typealias voor winrt::event_revoker<winrt::Windows::UI::Xaml::Controls::Primitives::IButtonBase>. Een vergelijkbaar patroon is van toepassing op alle C++/WinRT-gebeurtenissen. Elke Windows Runtime-gebeurtenis heeft een intrekfunctie-overload die een gebeurtenisintrekker retourneert, en het type van die intrekker is een lid van de gebeurtenisbron. Om een ander voorbeeld te nemen, de gebeurtenis CoreWindow::SizeChanged heeft een overbelaste registratiefunctie die een waarde retourneert van het type CoreWindow::SizeChanged_revoker.

U kunt overwegen handlers af te roepen in een paginanavigatiescenario. Als u herhaaldelijk naar een pagina navigeert en vervolgens weer naar buiten navigeert, kunt u alle handlers intrekken wanneer u van de pagina weg navigeert. Als alternatief, als u dezelfde pagina-instantie opnieuw gebruikt, controleert u de waarde van uw token en registreert u alleen als het nog niet is ingesteld (if (!m_token){ ... }). Een derde optie is het opslaan van een gebeurtenis intrekken op de pagina als gegevenslid. En een vierde optie, zoals verderop in dit onderwerp wordt beschreven, is het vastleggen van een sterke of zwakke verwijzing naar de dit object in uw lambda-functie.

Als uw gemachtigde voor automatisch intrekken niet kan worden geregistreerd

Als u probeert winrt::auto_revoke te specificeren bij het registreren van een gedelegeerde, en het resultaat een winrt::hresult_no_interface uitzondering is, betekent dit meestal dat de gebeurtenisbron geen zwakke verwijzingen ondersteunt. Dit is een veelvoorkomende situatie in de Windows.UI.Composition naamruimte, bijvoorbeeld. In dit geval kunt u de functie voor automatisch intrekken niet gebruiken. U moet terugvallen op het handmatig intrekken van uw gebeurtenis-handlers.

Typen gemachtigden voor asynchrone acties en bewerkingen

In de bovenstaande voorbeelden wordt gebruikgemaakt van het type RoutedEventHandler gedelegeerde, maar er zijn natuurlijk veel andere typen gemachtigden. Asynchrone acties en bewerkingen (met en zonder voortgang) hebben bijvoorbeeld voltooide en/of voortgangsgebeurtenissen die gedelegeerden van het bijbehorende type verwachten. De voortgangsgebeurtenis van een asynchrone bewerking met voortgang (dat wil zeggen alles wat IAsyncOperationWithProgress) implementeert, vereist een gemachtigde van het type AsyncOperationProgressHandler. Hier volgt een codevoorbeeld van het ontwerpen van een gemachtigde van dat type met behulp van een lambda-functie. In het voorbeeld ziet u ook hoe u een AsyncOperationWithProgressCompletedHandler delegaat maakt.

#include <winrt/Windows.Foundation.h>
#include <winrt/Windows.Web.Syndication.h>

using namespace winrt;
using namespace Windows::Foundation;
using namespace Windows::Web::Syndication;

void ProcessFeedAsync()
{
    Uri rssFeedUri{ L"https://blogs.windows.com/feed" };
    SyndicationClient syndicationClient;

    auto async_op_with_progress = syndicationClient.RetrieveFeedAsync(rssFeedUri);

    async_op_with_progress.Progress(
        [](
            IAsyncOperationWithProgress<SyndicationFeed,
            RetrievalProgress> const& /* sender */,
            RetrievalProgress const& args)
        {
            uint32_t bytes_retrieved = args.BytesRetrieved;
            // use bytes_retrieved;
        });

    async_op_with_progress.Completed(
        [](
            IAsyncOperationWithProgress<SyndicationFeed,
            RetrievalProgress> const& sender,
            AsyncStatus const /* asyncStatus */)
        {
            SyndicationFeed syndicationFeed = sender.GetResults();
            // use syndicationFeed;
        });

    // or (but this function must then be a coroutine, and return IAsyncAction)
    // SyndicationFeed syndicationFeed{ co_await async_op_with_progress };
}

Zoals in de bovenstaande opmerking over 'coroutine' wordt gesuggereerd, zult u het waarschijnlijk natuurlijker vinden om coroutines te gebruiken in plaats van een gedelegeerde met de voltooide gebeurtenissen van asynchrone acties en bewerkingen. Zie Gelijktijdigheid en asynchrone bewerkingen met C++/WinRT-voor meer informatie en codevoorbeelden.

Opmerking

Het is niet juist om meer dan één voltooiingshandler te implementeren voor een asynchrone actie of bewerking. U kunt of een enkele gedelegeerde hebben voor het evenement dat is voltooid, of u kunt co_await. Als u beide hebt, mislukt de tweede.

Als u zich houdt aan gedelegeerden in plaats van een coroutine, kunt u kiezen voor een eenvoudigere syntaxis.

async_op_with_progress.Completed(
    [](auto&& /*sender*/, AsyncStatus const /* args */)
{
    // ...
});

Typen gemachtigden die een waarde retourneren

Sommige typen gemachtigden moeten zelf een waarde retourneren. Een voorbeeld is ListViewItemToKeyHandler, waarmee een tekenreeks wordt geretourneerd. Hier volgt een voorbeeld van het ontwerpen van een gemachtigde van dat type (houd er rekening mee dat de lambda-functie een waarde retourneert).

using namespace winrt::Windows::UI::Xaml::Controls;

winrt::hstring f(ListView listview)
{
    return ListViewPersistenceHelper::GetRelativeScrollPosition(listview, [](IInspectable const& item)
    {
        return L"key for item goes here";
    });
}

Veilig toegang tot de deze aanwijzer met een gemachtigde voor gebeurtenisafhandeling

Als u een gebeurtenis verwerkt met de lidfunctie van een object of vanuit een lambda-functie binnen de lidfunctie van een object, moet u nadenken over de relatieve levensduur van de ontvanger van de gebeurtenis (het object dat de gebeurtenis verwerkt) en de gebeurtenisbron (het object dat de gebeurtenis opgeeft). Zie Sterke en zwakke verwijzingen in C++/WinRT-voor meer informatie en codevoorbeelden.

Belangrijke API's