Anteckning
Å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.
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.
Anmärkning
Information om hur du installerar och använder C++/WinRT Visual Studio-tillägget (VSIX) och NuGet-paketet (som tillsammans tillhandahåller projektmall och byggstöd) finns i Visual Studio-stöd 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 (UI) 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 för händelsehanterarens funktion (och en stub-implementering) till dina källfiler, som är redo att ersättas med din egen implementering.
Anmärkning
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::Windows::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 Tom app (C++/WinRT) 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.
Anmärkning
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.
Istället för att göra det deklarativt i markup kan du imperativt registrera en medlemsfunktion för att hantera en händelse. Det kanske inte är uppenbart i kodexemplet nedan, men argumentet till ButtonBase::Click-anropet är en instans av RoutedEventHandler delegering. I det här fallet använder vi RoutedEventHandler en överlagrad konstruktor som tar ett objekt och en pekare-till-medlemsfunktion.
// MainPage.cpp
MainPage::MainPage()
{
InitializeComponent();
myButton().Click({ this, &MainPage::ClickHandler });
}
Viktigt!
När ombudet registreras skickar kodexemplet ovan en rå denna pekare (som pekar på det aktuella 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 delegat.
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::Windows::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 en lambda; en annan en fri funktion; och en tredje (den vi använde ovan) tar ett objekt och en pekare-till-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::Windows::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.
Anmärkning
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 Language. I syntaxblocket i början av ämnet visas detta.
// Register
event_token KeyDown(KeyEventHandler const& handler) const;
Den här informationen anger att händelsen UIElement.KeyDown (ämnet vi är på) har en delegattyp av KeyEventHandler, eftersom det är den typ som du skickar när du registrerar en delegat med denna händelsetyp. Följ nu länken i avsnittet till den KeyEventHandler-delegaten av typen. 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::Windows::UI::Xaml::Input::KeyRoutedEventArgs const& e) const;
Som du ser måste delegaten deklareras för att ta 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>. Ombudet tar därför 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 en RoutedEventHandler delegering konstrueras från en lambda-funktion som behöver matcha syntaxen för operatorn för funktionsanrop 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::Windows::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 lagras token i structens privata datamedlem och dess hanterare återkallas i destructor.
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;
};
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).
Anmärkning
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 en händelsehanteringsdelegat.
När du registrerar en delegat 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). Händelse-återkallaren innehåller en svag referens till händelsekällan (objektet som genererar händelsen) åt dig. Manuellt kan du återkalla genom att anropa funktionen event_revoker::revoke member, men händelseupphävare anropar den funktionen automatiskt när den går utanför omfånget. Funktionen revokera kontrollerar om händelsekällan existerar och, om så är fallet, revokerar ditt ombud. 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::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;
};
Nedan visas syntaxblocket från dokumentationsavsnittet för ButtonBase::Klicka på händelse. 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 genom den tredje överbelastningen. Och du kan skicka samma typer av delegater till både Register och Revoke med event_revoker överlagringar för.
// 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;
Anmärkning
I kodexemplet ovan Button::Click_revoker är ett typalias för winrt::event_revoker<winrt::Windows::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 revokefunktion med överbelastning som returnerar en händelseåterkallare, och den återkallarens typ är medlem i händelsekällan. För att ta ett annat exempel, så har händelsen CoreWindow::SizeChanged en överlagrad registreringsfunktion som returnerar ett värde av typen CoreWindow::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 på sidan som en datamedlemsvariabel. Och ett fjärde alternativ, som beskrivs senare i det här avsnittet, är att fånga en stark eller svag referens till detta objekt 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 delegering, och resultatet ä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 Windows.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.
Delegerade typer 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örlopp) slutförts och/eller har förloppshändelser som förväntar delegater av motsvarande typ. Förloppshändelsen för en asynkron åtgärd med förlopp (vilket är allt som implementerar IAsyncOperationWithProgress) kräver ett ombud av typen AsyncOperationProgressHandler. Här är ett exempel på kod för att skapa en delegerad av den typen genom att använda en lambda-funktion. Exemplet visar också hur du författar en AsyncOperationWithProgressCompletedHandler delegeringshanterare.
#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 "korutin" ovan antyder, istället för att använda en delegat med slutförda händelser från asynkrona funktioner och operationer, kommer du förmodligen att finna det mer naturligt att använda koroutiner. Mer information och kodexempel finns i Samtidighet och asynkrona åtgärder med C++/WinRT.
Anmärkning
Det är inte korrekt att implementera fler än en kompletteringshanterare för en asynkron åtgärd eller operation. Du kan ha antingen en enda delegat för den färdiga händelsen, eller så kan du co_await den. Om du har båda, då kommer den andra att misslyckas.
Om du håller dig till deleger 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 ombudstyper 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::Windows::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 markörstrukturen
- winrt::implements::get_weak funktion
- winrt::implements::get_strong funktion