Notitie
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen u aan te melden of de directory te wijzigen.
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen de mappen te wijzigen.
Belangrijk
Bouwen met de Windows App SDK? In de code van dit artikel worden UWP-naamruimten (Windows.UI.Xaml) gebruikt. Als uw project gericht is op WinUI 3 (Windows App SDK), vervang dan overal Microsoft.UI.Xaml (en de bijbehorende naamruimten van Microsoft.UI.*). Zie UWP-API's toewijzen aan de Windows App SDK voor een volledige toewijzings- en UI-migratiehandleiding voor meer informatie.
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.
Note
Zie Visual Studio support for C++/WinRT voor informatie over het installeren en gebruiken van de Visual Studio-extensie (VSIX) en het NuGet-pakket voor C++/WinRT, die samen projectsjablonen en ondersteuning voor builds bieden.
Een gebeurtenis-handler toevoegen met behulp van Visual Studio
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 pictogram met de bliksemschicht om alle gebeurtenissen weer te geven die door dat besturingselement worden gegenereerd. 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.
Note
Doorgaans hoeven uw gebeurtenisafhandelaars niet te worden beschreven in uw MIDL-bestand (.idl). De XAML Designer voegt dus geen prototypen van de gebeurtenishandlerfunctie toe aan uw Midl-bestand. Het voegt ze alleen uw .h en .cpp bestanden toe.
Een gemachtigde registreren om een gebeurtenis af te handelen
Een eenvoudig voorbeeld is het afhandelen van de 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::Microsoft::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 project Blank App, Packaged (WinUI 3 in Desktop) in Visual Studio. Met de code myButton() wordt een gegenereerde accessorfunctie aangeroepen, die de knop retourneert met de naam myButton. Als u het x:Name element Knop wijzigt, wordt ook de naam van de gegenereerde accessorfunctie gewijzigd.
Note
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 exemplaar 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 markup te doen, kunt u op imperatieve wijze een memberfunctie registreren om een gebeurtenis te verwerken. Het is mogelijk niet duidelijk in het onderstaande codevoorbeeld, maar het argument voor de ButtonBase::Click-aanroep is een exemplaar van de gedelegeerde RoutedEventHandler . In dit geval gebruiken we de overload van de constructor van RoutedEventHandler die een object en een pointer naar een memberfunctie als argumenten accepteert.
// MainPage.cpp
MainPage::MainPage()
{
InitializeComponent();
myButton().Click({ this, &MainPage::ClickHandler });
}
Belangrijk
Bij het registreren van de delegate wordt in het bovenstaande codevoorbeeld een ruwe this-pointer doorgegeven (die verwijst naar het huidige object). Zie Als u een lidfunctie als gedelegeerde gebruikt voor meer informatie over het maken van een sterke of zwakke verwijzing naar het huidige object.
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::Microsoft::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 RoutedEventHandler te maken. Hieronder ziet u het syntaxisblok uit het documentatieonderwerp voor RoutedEventHandler (kies C++/WinRT in de vervolgkeuzelijst Taal in de rechterbovenhoek van de webpagina). Let op de verschillende constructors: de ene accepteert een lambda, een andere een vrije functie, en weer een andere (degene die we hierboven hebben gebruikt) accepteert een object en een pointer naar een memberfunctie.
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::Microsoft::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 onze MainPage::ClickHandler.
Note
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 Taal . In het syntaxisblok aan het begin van het onderwerp ziet u dit.
// Register
event_token KeyDown(KeyEventHandler const& handler) const;
Met deze informatie wordt aangegeven dat de gebeurtenis UIElement.KeyDown (het onderwerp waaraan we werken) een gedelegeerde van KeyEventHandler heeft, omdat dit het type is dat u doorgeeft wanneer u een gemachtigde registreert bij dit gebeurtenistype. Volg nu de link bij het onderwerp naar dat type KeyEventHandler delegate. Hier bevat het syntaxisblok een operator voor functie-aanroep. En zoals hierboven vermeld, geeft dat aan welke parameters je delegate moet hebben.
void operator()(
winrt::Windows::Foundation::IInspectable const& sender,
winrt::Microsoft::UI::Xaml::Input::KeyRoutedEventArgs const& e) const;
Zoals u kunt zien, moet de delegate zo worden gedeclareerd dat deze een IInspectable als afzender en een exemplaar van de klasse KeyRoutedEventArgs als argument accepteert.
Laten we een ander voorbeeld bekijken van de gebeurtenis Popup.Closed. Het gedelegeerdetype is EventHandler<IInspectable>. Uw gedelegeerde neemt dus een IInspectable als afzender en een andere IInspectable (omdat dat de parameter van het type EventHandler is) als argument.
Als u niet veel werk verricht in uw gebeurtenis-handler, kunt u een lambda-functie gebruiken in plaats van een memberfunctie. 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::Microsoft::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. Maar in dit volgende codevoorbeeld wordt het token opgeslagen in het private gegevenslid van de struct en wordt de handler in de destructor afgemeld.
struct Example : ExampleT<Example>
{
Example(winrt::Microsoft::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::Microsoft::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).
Note
Wanneer een gebeurtenisbron de gebeurtenissen synchroon genereert, kunt u de handler intrekken en erop vertrouwen dat u geen gebeurtenissen meer ontvangt. Maar bij asynchrone gebeurtenissen kan zelfs na het intrekken van de registratie (en vooral wanneer dat in de destructor gebeurt) een gebeurtenis die al onderweg is uw object nog bereiken nadat de destructie ervan al is begonnen. Een plek vinden om u uit te schrijven voordat het object wordt vernietigd, kan het probleem mogelijk beperken, of raadpleeg voor een robuuste oplossing Veilige toegang tot de this-aanwijzer met een delegate voor gebeurtenisafhandeling.
Als u een gemachtigde registreert, kunt u ook winrt::auto_revoke (een waarde van het type winrt::auto_revoke_t) opgeven om een gebeurtenis-intrekking aan te vragen (van het type winrt::event_revoker). De eventrevoker houdt voor u een zwakke verwijzing naar de eventbron vast (het object dat de gebeurtenis activeert). U kunt deze handmatig intrekken door de lidfunctie event_revoker::revoke aan te roepen; maar de event-revoker roept die functie zelf automatisch aan wanneer deze buiten scope raakt. Met de functie intrekken wordt gecontroleerd 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::Microsoft::UI::Xaml::Controls::Button button)
{
m_event_revoker = button.Click(
winrt::auto_revoke,
[this](IInspectable const& /* sender */,
RoutedEventArgs const& /* args */)
{
// ...
});
}
private:
winrt::Microsoft::UI::Xaml::Controls::Button::Click_revoker m_event_revoker;
};
Hieronder ziet u het syntaxisblok uit het documentatieonderwerp voor de ButtonBase::Click-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. En u kunt aan zowel de register- als de revoke with event_revoker-overloads dezelfde soorten gedelegeerden doorgeven.
// Register
winrt::event_token Click(winrt::Microsoft::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::Microsoft::UI::Xaml::RoutedEventHandler const& handler) const;
Note
In het bovenstaande Button::Click_revoker codevoorbeeld is een typealias voor winrt::event_revoker<winrt::Microsoft::UI::Xaml::Controls::Primitives::IButtonBase>. Een vergelijkbaar patroon is van toepassing op alle C++/WinRT-gebeurtenissen. Elke Windows Runtime-gebeurtenis heeft een overload van de intrekkingsfunctie die een event-revoker retourneert, en het type van die revoker is een member van de gebeurtenisbron. Als u een ander voorbeeld wilt nemen, heeft de gebeurtenis Window::SizeChanged een overbelasting van de registratiefunctie die een waarde van het type Window::SizeChanged_revoker retourneert.
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, controleer dan de waarde van uw token en registreer deze alleen als deze 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 het object in uw lambda-functie.
Als uw gemachtigde voor automatisch intrekken niet kan worden geregistreerd
Als u winrt::auto_revoke wilt opgeven bij het registreren van een gemachtigde en het resultaat een winrt::hresult_no_interface uitzondering is, betekent dit meestal dat de gebeurtenisbron geen zwakke verwijzingen ondersteunt. Dat is een veelvoorkomende situatie in de Microsoft. UI. Samenstellingsnaamruimte bijvoorbeeld. In dit geval kunt u de functie voor automatisch intrekken niet gebruiken. U zult moeten terugvallen op het handmatig intrekken van uw eventhandlers.
Typen gemachtigden voor asynchrone acties en bewerkingen
In de bovenstaande voorbeelden wordt het type Gedelegeerde RoutedEventHandler gebruikt, maar er zijn natuurlijk veel andere gedelegeerdentypen. Asynchrone acties en bewerkingen (met en zonder voortgang) zijn bijvoorbeeld voltooid 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 wordt ook getoond hoe u een AsyncOperationWithProgressCompletedHandler-delegate schrijft.
#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 de bovenstaande opmerking over 'coroutine' al aangeeft, zult u waarschijnlijk merken dat het natuurlijker is om coroutines te gebruiken in plaats van een delegate te gebruiken bij de voltooiingsgebeurtenissen van asynchrone acties en bewerkingen. Zie Gelijktijdigheid en asynchrone bewerkingen met C++/WinRT voor meer informatie en codevoorbeelden.
Note
Het is niet juist om meer dan één voltooiingshandler te implementeren voor een asynchrone actie of bewerking. U kunt één gedelegeerde hebben voor de voltooide gebeurtenis, of u kunt co_await deze. 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 */)
{
// ...
});
Delegatetypen die een waarde teruggeven
Sommige typen gemachtigden moeten zelf een waarde retourneren. Een voorbeeld is ListViewItemToKeyHandler, dat een tekenreeks retourneert. 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::Microsoft::UI::Xaml::Controls;
winrt::hstring f(ListView listview)
{
return ListViewPersistenceHelper::GetRelativeScrollPosition(listview, [](IInspectable const& item)
{
return L"key for item goes here";
});
}
Veilige toegang tot 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
- winrt::auto_revoke_t marker-struct
- winrt::implements::get_weak-functie
- winrt::implements::get_strong-functie
Verwante onderwerpen
Windows developer