Megjegyzés
Az oldalhoz való hozzáféréshez engedély szükséges. Megpróbálhat bejelentkezni vagy módosítani a címtárat.
Az oldalhoz való hozzáféréshez engedély szükséges. Megpróbálhatja módosítani a címtárat.
Fontos
A Windows App SDK-val fejleszt? A cikk kódja UWP-(Windows.UI.Xaml) névtereket használ. Ha a projekt a WinUI 3 -at (Windows App SDK) célozza meg, cserélje le Microsoft.UI.Xaml (és a kapcsolódó Microsoft.UI.* névterek) egészére. További részletekért tekintse meg az UWP API-k Windows App SDK való leképezését ismertető teljes leképezési és felhasználói felületi migrálási útmutatót.
Ez a témakör bemutatja, hogyan regisztrálhat és vonhat vissza eseménykezelő meghatalmazottakat a C++/WinRT használatával. Az eseményeket bármilyen szabványos C++ függvényszerű objektummal kezelheti.
Note
A C++/WinRT Visual Studio bővítmény (VSIX) és a NuGet-csomag telepítésével és használatával kapcsolatos információkért (amelyek együttesen nyújtanak projektsablont és buildtámogatást) lásd Visual Studio C++/WinRT támogatását.
Eseménykezelő hozzáadása Visual Studio használatával
Az eseménykezelő projekthez való hozzáadásának kényelmes módja az XAML Designer felhasználói felületének (UI) használata Visual Studio. Ha meg van nyitva az XAML-oldal az XAML-tervezőben, válassza ki azt a vezérlőt, amelynek eseményét kezelni szeretné. A vezérlő tulajdonságlapján kattintson a villámgyors ikonra a vezérlő által létrehozott összes esemény listázásához. Ezután kattintson duplán a kezelni kívánt eseményre; például OnClicked.
Az XAML Designer hozzáadja a megfelelő eseménykezelő függvény prototípusát (és egy stubmegvalósítást) a forrásfájljaiba, amelyet aztán a saját megvalósítására cserélhet.
Note
Az eseménykezelőket általában nem kell leírni a Midl-fájlban (.idl). Az XAML Designer tehát nem ad hozzá eseménykezelő függvény prototípusokat a Midl-fájlhoz. Csak a(z) Ön .h és .cpp fájljaihoz adja hozzá őket.
Meghatalmazott regisztrálása esemény kezeléséhez
Egy egyszerű példa egy gomb kattintási eseményének kezelésére. Általában XAML-jelölőnyelv használatával szokás egy tagfüggvényt regisztrálni az esemény kezelésére, így.
// 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"));
}
A fenti kód a Visual Studio Blank App, Packaged (WinUI 3 in Desktop) projektből származik. A kód myButton() meghív egy létrehozott kiegészítő függvényt, amely a myButton nevű gombot adja vissza. Ha módosítja annak a Button elemnek a x:Name elemét, akkor a létrehozott elérőfüggvény neve is megváltozik.
Note
Ebben az esetben az eseményforrás (az eseményt felhozó objektum) a myButton nevű gomb. Az esemény címzettje (az eseményt kezelő objektum) pedig a MainPage egy példánya. A témakör későbbi részében további információ található az eseményforrások és az esemény címzettjeinek élettartamának kezeléséről.
Ahelyett, hogy ezt deklaratív módon a jelölőnyelvben tenné meg, imperatív módon is regisztrálhat egy tagfüggvényt egy esemény kezelésére. Előfordulhat, hogy az alábbi kód példájából nem derül ki, de a ButtonBase::Click hívás argumentuma a RoutedEventHandler delegáltjának egy példánya. Ebben az esetben a RoutedEventHandler konstruktor túlterhelését használjuk, amely egy objektumot és egy mutató–tag függvényt vesz igénybe.
// MainPage.cpp
MainPage::MainPage()
{
InitializeComponent();
myButton().Click({ this, &MainPage::ClickHandler });
}
Fontos
A delegált regisztrálásakor a fenti kódpélda egy nyers this mutatót ad át (amely az aktuális objektumra mutat). Ha meg szeretné tudni, hogyan hozhat létre erős vagy gyenge hivatkozást az aktuális objektumra, olvassa el a Tagfüggvény delegálásként való használatát ismertető témakört.
Íme egy példa, amely statikus tagfüggvényt használ; vegye figyelembe az egyszerűbb szintaxist.
// 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 */) { ... }
A RoutedEventHandler más módokon is létrehozható. Az alábbiakban a RoutedEventHandler dokumentációs témaköréből vett szintaxisblokk látható (a weblap jobb felső sarkában található Nyelv legördülő menüből válassza a C++/WinRT elemet). Figyelje meg a különböző konstruktorokat: az egyik lambdát fogad; egy másik egy szabad függvényt; egy harmadik pedig (amelyet fent használtunk) egy objektumot és egy tagfüggvényre mutató pointert fogad.
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;
};
A függvényhívási operátor szintaxisa is hasznos lehet. Megmutatja, hogy milyen paramétereknek kell lennie a meghatalmazottnak. Mint látható, ebben az esetben a függvényhívás operátor szintaxisa megegyezik a MainPage::ClickHandler paraméterével.
Note
Egy adott esemény esetében a meghatalmazott részleteinek és a meghatalmazott paramétereinek megállapításához először keresse meg az esemény dokumentációs témakörét. Vegyük példaként a UIElement.KeyDown eseményt . Keresse fel ezt a témakört, és válassza a Nyelv legördülő menü C++/WinRT elemét. A témakör elején lévő szintaxisblokkban ezt fogja látni.
// Register
event_token KeyDown(KeyEventHandler const& handler) const;
Ezek az információk azt jelzik, hogy a UIElement.KeyDown esemény (a témakör, amellyel foglalkozunk) delegált típusú KeyEventHandlerrel rendelkezik, mivel ezt a típust adja át, amikor egy meghatalmazottat regisztrál ezzel az eseménytípussal. Tehát most kövesse a témakörben található hivatkozást ahhoz a KeyEventHandler delegate típushoz. Itt a szintaxisblokk egy függvényhívási operátort tartalmaz. És ahogy fentebb említettük, ez megmutatja, hogy milyen paraméterekkel kell rendelkeznie a meghatalmazottnak.
void operator()(
winrt::Windows::Foundation::IInspectable const& sender,
winrt::Microsoft::UI::Xaml::Input::KeyRoutedEventArgs const& e) const;
Mint látható, a meghatalmazottnak deklarálnia kell, hogy egy IInspectable-t vegyen fel feladóként, és a KeyRoutedEventArgs osztály egy példányát argsként.
Egy másik példához tekintsük meg a Popup.Closed eseményt. A delegálttípusa a következő: EventHandler<IInspectable>. Tehát a delegált a küldőként egy IInspectable-et, az argumentumokként pedig egy másik IInspectable-et kap (mert ez a EventHandler típusparamétere).
Ha nem végez sok munkát az eseménykezelőben, akkor a lambda függvényt használhatja tagfüggvény helyett. Előfordulhat, hogy az alábbi kód példájából nem derül ki, de a RoutedEventHandler delegált egy lambda függvényből készül, amelynek ismét meg kell egyeznie a függvényhívási operátor fent ismertetett szintaxisával.
MainPage::MainPage()
{
InitializeComponent();
myButton().Click([this](IInspectable const& /* sender */, RoutedEventArgs const& /* args */)
{
myButton().Content(box_value(L"Clicked"));
});
}
Dönthet úgy is, hogy egy kicsit egyértelműbb lesz a delegált létrehozásakor. Ha például át szeretné adni, vagy többször szeretné használni.
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);
}
Regisztrált meghatalmazott visszavonása
Amikor regisztrál egy delegáltat, általában kap egy tokent. Ezt követően ezzel a tokennel visszavonhatja a delegáltat; ez azt jelenti, hogy a delegált le lesz iratva az eseményről, és nem hívódik meg, ha az esemény újra kiváltódik.
Az egyszerűség kedvéért a fenti kód példák egyike sem mutatta meg, hogyan kell ezt megtenni. Ez a következő kódpélda azonban a tokent a struktúra privát adattagjában tárolja, és a destruktorában érvényteleníti a kezelőjét.
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;
};
Erős hivatkozás helyett, mint a fenti példában, a gomb gyenge hivatkozását tárolhatja (lásd : Erős és gyenge hivatkozások a C++/WinRT-ben).
Note
Ha egy eseményforrás szinkron módon váltja ki az eseményeit, eltávolíthatja a kezelőjét, és biztos lehet benne, hogy nem kap több eseményt. Az aszinkron események esetében azonban még a visszavonás után is (és különösen a destruktoron belüli visszavonáskor) egy repülés közbeni esemény a destruktúra megkezdése után is elérheti az objektumot. A megsemmisítés előtti leiratkozási hely megtalálása csökkentheti a problémát, vagy egy robusztus megoldásért tekintse meg a mutató biztonságos elérését egy eseménykezelő meghatalmazottal.
Alternatív megoldásként delegált regisztrálásakor megadhatja a winrt::auto_revoke értéket (amelynek típusa winrt::auto_revoke_t), hogy winrt::event_revoker típusú esemény-visszavonót kérjen. Az esemény-visszavonó gyenge hivatkozást őriz meg Ön helyett az eseményforrásra (arra az objektumra, amely kiváltja az eseményt). Manuálisan is visszavonhatja a műveletet a event_revoker::revoke tagfüggvény meghívásával; az esemény-visszavonó objektum azonban automatikusan maga hívja meg ezt a függvényt, amikor kikerül a hatókörből. A visszavonási függvény ellenőrzi, hogy az eseményforrás még létezik-e, és ha igen, visszavonja a meghatalmazottat. Ebben a példában nincs szükség az eseményforrás tárolására, és nincs szükség destruktorra.
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;
};
Az alábbiakban a ButtonBase::Click esemény dokumentációs témájából származó szintaxisblokk látható. A három különböző regisztrációs és visszavonási függvényt jeleníti meg. Pontosan láthatja, hogy milyen típusú esemény-visszahúzót kell deklarálnia a harmadik túlterheléstől. Ugyanolyan típusú delegátumokat adhat át mind a register, mind a revoke with event_revoker túlterheléseknek.
// 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
A fenti kódpéldában a(z) Button::Click_revoker a(z) winrt::event_revoker<winrt::Microsoft::UI::Xaml::Controls::Primitives::IButtonBase> típusaliasa. Hasonló minta vonatkozik az összes C++/WinRT-eseményre. Minden Windows-futtatókörnyezet-esemény rendelkezik a revoke függvény egy olyan túlterhelt változatával, amely egy esemény-visszavonó objektumot ad vissza, és ennek a visszavonó objektumnak a típusa az eseményforrás egyik tagtípusa. Ezért egy másik példaként a Window::SizeChanged eseménynek van egy regisztrációs függvény túlterhelése, amely egy Ablak::SizeChanged_revoker típusú értéket ad vissza.
Lapnavigációs forgatókönyv esetén megfontolhatja a kezelők visszavonását. Ha ismételten megnyit egy oldalt, majd kilép onnan, akkor az oldalról való elnavigáláskor eltávolíthatja az eseménykezelőket. Alternatív megoldásként, ha ugyanazt az oldalpéldányt használja, ellenőrizze a token értékét, és csak akkor végezze el a regisztrációt, ha az még nincs beállítva (if (!m_token){ ... }). A harmadik lehetőség az, hogy egy esemény-visszavonót adattagként tárol a lapon. A témakör későbbi részében ismertetett negyedik lehetőség pedig az, hogy rögzítsünk egy erős vagy gyenge hivatkozást az objektumra a lambda függvényben.
Ha az automatikus visszavonási meghatalmazott nem regisztrál
Ha megpróbálja megadni a(z) winrt::auto_revoke értéket egy delegátus regisztrálásakor, és ennek eredményeként winrt::hresult_no_interface kivétel keletkezik, az általában azt jelenti, hogy az eseményforrás nem támogatja a gyenge hivatkozásokat. Ez gyakori helyzet a Microsoft.UI.Composition névtérben, például. Ebben az esetben nem használhatja az automatikus visszavonás funkciót. Kénytelen lesz manuálisan visszavonni az eseménykezelőit.
Aszinkron műveletek és műveletek delegálási típusai
A fenti példák a RoutedEventHandler delegálttípust használják, de természetesen számos más meghatalmazotttípus is létezik. Például az aszinkron műveleteknek és műveleteknek (folyamatjelzéssel és anélkül) vannak befejezési és/vagy folyamatjelző eseményeik, amelyek a megfelelő típusú delegáltakat várják. Például egy előrehaladással rendelkező aszinkron művelet előrehaladási eseményéhez (amely az IAsyncOperationWithProgress implementálását valósítja meg) egy AsyncOperationProgressHandler típusú delegáltra van szükség. Íme egy kódpélda arra, hogyan definiálhat egy ilyen típusú delegátumot egy lambdafüggvénnyel. A példa azt is bemutatja, hogyan kell megírni egy AsyncOperationWithProgressCompletedHandler delegáltat.
#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 };
}
Ahogy a fenti „coroutine” megjegyzés is sugallja, az aszinkron tevékenységek és műveletek befejezési eseményeihez használt delegált helyett valószínűleg természetesebbnek fogja találni a korutinok használatát. További részletek és példakódok: Egyidejűség és aszinkron műveletek a C++/WinRT használatával.
Note
Nem helyes, ha több befejezéskezelőt implementál egy aszinkron művelethez vagy művelethez. A befejezési eseményhez vagy egyetlen delegált tartozhat, vagy co_await delegálhatja. Ha mindkettő van, akkor a második sikertelen lesz.
Ha a coroutine helyett a delegáltaknál marad, akkor egyszerűbb szintaxist választhat.
async_op_with_progress.Completed(
[](auto&& /*sender*/, AsyncStatus const /* args */)
{
// ...
});
Delegálási típusok, amelyek értéket adnak vissza
Egyes meghatalmazotttípusoknak maguknak kell visszaadniuk egy értéket. Ilyen például a ListViewItemToKeyHandler, amely egy sztringet ad vissza. Íme egy példa egy ilyen típusú delegált megírására (vegye figyelembe, hogy a lambda függvény egy értéket ad vissza).
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";
});
}
Az egérmutató biztonságos elérése eseménykezelő meghatalmazottal
Ha egy eseményt egy objektum tagfüggvényével vagy egy lambda függvénnyel kezel egy objektum tagfüggvényén belül, akkor az esemény címzettjének (az eseményt kezelő objektumnak) és az esemény forrásának (az eseményt növelő objektumnak) relatív élettartamára kell gondolnia. További információkért és kódpéldákért lásd a C++/WinRT erős és gyenge hivatkozásait.
Fontos API-k
- winrt::auto_revoke_t jelölőstruktúra
- winrt::implements::get_weak függvény
- winrt::implements::get_strong függvény
Kapcsolódó témakörök
Windows developer