Kommentar
Åtkomst till den här sidan kräver auktorisering. Du kan prova att logga in eller ändra kataloger.
Åtkomst till den här sidan kräver auktorisering. Du kan prova att ändra kataloger.
Viktigt!
Bygga med Windows App SDK? Den här artikelns kod använder UWP-namnområden (Windows.UI.Xaml). Om ditt projekt är inriktat på WinUI 3 (Windows App SDK), ersätt Microsoft.UI.Xaml (och relaterade Microsoft.UI.*-namnområden) genomgående. Mer information finns i Mappa UWP-API:er till Windows App SDK för en fullständig migreringsguide för mappning och användargränssnitt.
Det här avsnittet visar hur du registrerar och återkallar ombud för händelsehantering med hjälp av C++/WinRT. Du kan hantera en händelse med valfritt C++-standardfunktionsliknande objekt.
Note
Information om hur du installerar och använder VSIX (C++/WinRT Visual Studio Extension) och NuGet-paketet (som tillsammans tillhandahåller projektmall och byggstöd) finns i Visual Studio support för C++/WinRT.
Använda Visual Studio för att lägga till en händelsehanterare
Ett praktiskt sätt att lägga till en händelsehanterare i projektet är att använda användargränssnittet för XAML Designer i Visual Studio. När XAML-sidan är öppen i XAML Designer väljer du den kontroll vars händelse du vill hantera. På egenskapssidan för kontrollen klickar du på blixtikonen för att visa en lista över alla händelser som kommer från kontrollen. Dubbelklicka sedan på den händelse som du vill hantera. till exempel OnClicked.
XAML Designer lägger till lämplig prototyp av händelsehanterarens funktion (och en stub-implementering) till dina källfiler, som är redo att ersättas med din egen implementering.
Note
Vanligtvis behöver dina händelsehanterare inte beskrivas i Midl-filen (.idl). Därför lägger XAML-designern inte till prototyper för händelsehanterarfunktionen i Midl-filen. Den lägger bara till dina .h filer och .cpp filer.
Registrera ett ombud för att hantera en händelse
Ett enkelt exempel är att hantera en knapps klickhändelse. Det är vanligt att använda XAML-markering för att registrera en medlemsfunktion för att hantera händelsen, så här.
// 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"));
}
Koden ovan hämtas från projektet Blank App, Packaged (WinUI 3 in Desktop) i Visual Studio. Koden myButton() anropar en genererad accessor-funktion som returnerar knappen som vi gav myButton namnet. Om du ändrar x:Nameknappelementets namn ändras även namnet på den genererade accessorfunktionen.
Note
I det här fallet är händelsekällan (objektet som genererar händelsen) knappenmed namnet myButton. Och händelsemottagaren (objektet som hanterar händelsen) är en instans av MainPage. Det finns mer information senare i det här avsnittet om hur du hanterar livslängden för händelsekällor och händelsemottagare.
I stället för att göra det deklarativt i markup-kod kan du registrera en medlemsfunktion imperativt som hanterar en händelse. Det kanske inte är uppenbart i kodexemplet nedan, men argumentet till ButtonBase::Click-anropet är en instans av RoutedEventHandler-ombudet . I det här fallet använder vi den överlagrade konstruktorn RoutedEventHandler som tar emot ett objekt och en pekare till en medlemsfunktion.
// MainPage.cpp
MainPage::MainPage()
{
InitializeComponent();
myButton().Click({ this, &MainPage::ClickHandler });
}
Viktigt!
När du registrerar delegaten skickar kodexemplet ovan en rå this-pekare (som pekar på det nuvarande objektet). Information om hur du upprättar en stark eller svag referens till det aktuella objektet finns i Om du använder en medlemsfunktion som ombud.
Här är ett exempel som använder en statisk medlemsfunktion. observera den enklare syntaxen.
// 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 */) { ... }
Det finns andra sätt att skapa en RoutedEventHandler. Nedan visas syntaxblocket från dokumentationsavsnittet för RoutedEventHandler (välj C++/WinRT i listrutan Språk i det övre högra hörnet på webbsidan). Observera de olika konstruktorerna: en tar emot en lambda, en annan en frifunktion, och ytterligare en (den vi använde ovan) tar emot ett objekt och en pekare till en medlemsfunktion.
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;
};
Syntaxen för funktionsanropsoperatorn är också användbar att se. Den visar vad ombudets parametrar måste vara. Som du ser matchar syntaxen för funktionsanropsoperatorn i det här fallet parametrarna för vår MainPage::ClickHandler.
Note
För varje given händelse går du först till dokumentationsavsnittet för själva händelsen för att ta reda på information om dess ombud och ombudets parametrar. Vi tar händelsen UIElement.KeyDown som ett exempel. Besök det ämnet och välj C++/WinRT i listrutan Språk . I syntaxblocket i början av ämnet visas detta.
// Register
event_token KeyDown(KeyEventHandler const& handler) const;
Den här informationen visar att händelsen UIElement.KeyDown (det ämne vi behandlar) har delegeringstypen KeyEventHandler, eftersom det är den typ som du anger när du registrerar en delegering för den här händelsetypen. Följ nu länken i avsnittet till delegattypen KeyEventHandler. Här innehåller syntaxblocket en funktionsanropsoperator. Och som nämnts ovan talar det om för dig vad ombudets parametrar måste vara.
void operator()(
winrt::Windows::Foundation::IInspectable const& sender,
winrt::Microsoft::UI::Xaml::Input::KeyRoutedEventArgs const& e) const;
Som du ser måste delegaten deklareras så att den accepterar en IInspectable som avsändare och en instans av klassen KeyRoutedEventArgs som argument.
Om du vill ta ett annat exempel ska vi titta på händelsen Popup.Closed. Dess ombudstyp är EventHandler<IInspectable>. Så tar din delegat en IInspectable som avsändare och en annan IInspectable (eftersom det är EventHandler:s typparameter) som argument.
Om du inte utför mycket arbete i händelsehanteraren kan du använda en lambda-funktion i stället för en medlemsfunktion. Återigen kanske det inte är uppenbart från kodexemplet nedan, men ett RoutedEventHandler-ombud skapas från en lambda-funktion som återigen måste matcha syntaxen för funktionsanropsoperatorn som vi diskuterade ovan.
MainPage::MainPage()
{
InitializeComponent();
myButton().Click([this](IInspectable const& /* sender */, RoutedEventArgs const& /* args */)
{
myButton().Content(box_value(L"Clicked"));
});
}
Du kan välja att vara lite mer explicit när du skapar ditt ombud. Om du till exempel vill skicka runt den eller använda den mer än en gång.
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);
}
Återkalla ett registrerat ombud
När du registrerar ett ombud returneras vanligtvis en token till dig. Därefter kan du använda den token för att återkalla ditt ombud. vilket innebär att ombudet avregistreras från händelsen och inte anropas om händelsen aktiveras igen.
För enkelhetens skull visade inget av kodexemplen ovan hur du gör det. Men i nästa kodexempel lagrar strukturen token i sin privata datamedlem och avregistrerar sin hanterare i destruktorn.
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;
};
I stället för en stark referens, som i exemplet ovan, kan du lagra en svag referens till knappen (se Starka och svaga referenser i C++/WinRT).
Note
När en händelsekälla genererar sina händelser synkront kan du återkalla din hanterare och vara säker på att du inte får fler händelser. Men för asynkrona händelser, även efter att ha återkallats (och särskilt när du återkallar inom destruktorn), kan en händelse under flygning nå objektet när det har börjat förstöras. Om du hittar en plats där du kan avbryta prenumerationen innan destruktionen kan problemet åtgärdas, eller för en robust lösning, se Säker åtkomst till den här pekaren med ett ombud för händelsehantering.
När du registrerar ett ombud kan du också ange winrt::auto_revoke (vilket är ett värde av typen winrt::auto_revoke_t) för att begära en händelse-återkallare (av typen winrt::event_revoker). Återkallaren för händelsen har en svag referens till händelsekällan (objektet som utlöser händelsen) åt dig. Du kan återkalla den manuellt genom att anropa medlemsfunktionen event_revoker::revoke; men event revokern anropar den funktionen automatiskt när den går ur scope. Funktionen återkalla kontrollerar om händelsekällan fortfarande finns och återkallar i så fall ombudet. I det här exemplet behöver du inte lagra händelsekällan och inget behov av en destructor.
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;
};
Nedan visas syntaxblocket från dokumentationsavsnittet för händelsen ButtonBase::Click . Den visar de tre olika funktionerna för registrering och återkallande. Du kan se exakt vilken typ av händelse-återkallare du behöver deklarera från den tredje överlagringen. Du kan också skicka samma typer av delegater till både register- och revoke med event_revoker-överlagringarna.
// 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
I kodexemplet ovan Button::Click_revoker är ett typalias för winrt::event_revoker<winrt::Microsoft::UI::Xaml::Controls::Primitives::IButtonBase>. Ett liknande mönster gäller för alla C++/WinRT-händelser. Varje Windows Runtime-händelse har en överlagring av funktionen revoke som returnerar en händelseåterkallare, och typen för den återkallaren är en medlem i händelsekällan. För att ta ett annat exempel har händelsen Window::SizeChanged en överlagring av registreringsfunktionen som returnerar ett värde av typen Window::SizeChanged_revoker.
Du kan överväga att återkalla hanterare i ett sidnavigeringsscenario. Om du flera gånger navigerar till en sida och sedan backar ur kan du återkalla alla hanterare när du navigerar bort från sidan. Om du använder samma sidinstans igen kan du också kontrollera värdet för din token och bara registrera om den ännu inte har angetts (if (!m_token){ ... }). Ett tredje alternativ är att lagra en händelseåterkallare i sidan som en medlemsvariabel. Och ett fjärde alternativ, som beskrivs senare i det här ämnet, är att fånga en stark eller svag referens till det här objektet i din lambda-funktion.
Om ombudet för automatisk återkallande inte kan registrera sig
Om du försöker ange winrt::auto_revoke när du registrerar en delegat, och du får ett winrt::hresult_no_interface-undantag, innebär det vanligtvis att händelsekällan inte stöder svaga referenser. Det är en vanlig situation i namnområdet Microsoft.UI.Composition, till exempel. I det här fallet kan du inte använda funktionen för automatisk återkallande. Du måste återgå till att manuellt återkalla dina händelsehanterare.
Delegattyper för asynkrona åtgärder och operationer
Exemplen ovan använder ombudstypen RoutedEventHandler , men det finns naturligtvis många andra ombudstyper. Till exempel har asynkrona åtgärder och operationer (med och utan förloppsindikering) slutförandehändelser och/eller förloppshändelser som förväntar sig delegater av motsvarande typ. Förloppshändelsen för en asynkron åtgärd med förlopp (vilket är allt som implementerar IAsyncOperationWithProgress) kräver till exempel ett ombud av typen AsyncOperationProgressHandler. Här är ett kodexempel på hur du skriver en delegat av den här typen med hjälp av en lambda-funktion. Exemplet visar också hur du definierar en AsyncOperationWithProgressCompletedHandler-delegat.
#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 };
}
Som kommentaren om "coroutine" ovan antyder kommer du förmodligen att tycka att det känns mer naturligt att använda korutiner i stället för att använda en delegat med slutförandehändelserna för asynkrona åtgärder och operationer. Mer information och kodexempel finns i Samtidighet och asynkrona åtgärder med C++/WinRT.
Note
Det är inte korrekt att implementera fler än en slutförandehanterare för en asynkron åtgärd eller åtgärd. Du kan antingen ha en enda delegat för dess slutförandehändelse, eller så kan du co_await den. Om du har båda, kommer den andra att misslyckas.
Om du använder delegater i stället för en korutin, kan du välja en enklare syntax.
async_op_with_progress.Completed(
[](auto&& /*sender*/, AsyncStatus const /* args */)
{
// ...
});
Delegattyper som returnerar ett värde
Vissa delegattyper måste själva returnera ett värde. Ett exempel är ListViewItemToKeyHandler, som returnerar en sträng. Här är ett exempel på hur du redigerar ett ombud av den typen (observera att lambda-funktionen returnerar ett värde).
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";
});
}
Åtkomst till den här pekaren på ett säkert sätt med ett ombud för händelsehantering
Om du hanterar en händelse med ett objekts medlemsfunktion, eller inifrån en lambda-funktion inuti ett objekts medlemsfunktion, måste du tänka på händelsemottagarens relativa livslängd (objektet som hanterar händelsen) och händelsekällan (objektet som lyfter händelsen). Mer information och kodexempel finns i Starka och svaga referenser i C++/WinRT.
Viktiga API:er
- winrt::auto_revoke_t marker struct
- winrt::implements::get_weak function
- winrt::implements::get_strong funktion
Relaterade ämnen
Windows developer