Megosztás a következőn keresztül:


Események kezelése meghatalmazottak használatával a C++/WinRT-ben

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.

Megjegyzés:

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 támogatása a C++/WinRT.

Eseménykezelő hozzáadása a Visual Studió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 a Visual Studióban. 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 csonk-implementációt) a forrásfájlokhoz, készen áll arra, hogy Ön lecserélje a saját implementációjával.

Megjegyzés:

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 ezeket adja hozzá: a .h és a .cpp fájlokat.

Regisztráljon egy meghatalmazottat egy esemény kezelésére

Egy egyszerű példa egy gomb kattintási eseményének kezelésére. A XAML jelölés használata jellemző, hogy egy tagfüggvényt regisztráljunk 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::Windows::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 (C++/WinRT) projektjéből származik. A kód egy létrehozott kiegészítő függvényt hív meg, amely visszaadja a Gomb, amelyet a myButtonelneveztünk. Ha módosítja annak a x:Name-elemnek a , akkor a létrehozott elérési függvény neve megváltozik.

Megjegyzés:

Ebben az esetben az eseményforrás (az eseményt kiváltó objektum) a gomb, amelynek neve myButton. Az esemény címzettje (az eseményt kezelő objektum) pedig a MainPagepé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 deklaratív módon végzi el a jelölőnyelvben, imperatív módon regisztrálhat egy tagfüggvényt egy esemény kezeléséhez. Lehet, hogy az alábbi kód példájából nem egyértelmű, de a ButtonBase::Click hívásnál az argumentum a RoutedEventHandler delegált egy példánya. Ebben az esetben a RoutedEventHandler konstruktort 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 meghatalmazott regisztrálásakor a fenti kódpéldában egy nyers ez a mutatót ad át (az aktuális objektumra mutatva). Ha szeretné megtudni, hogyan hozhat létre erős vagy gyenge hivatkozást az aktuális objektumra, olvassa el: Ha tagfüggvényt használ delegáltként.

Í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::Windows::UI::Xaml::RoutedEventArgs const& args);

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

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

Különböző módokon is létrehozható egy RoutedEventHandler. Az alábbiakban a RoutedEventHandler dokumentációs témaköréből vett szintaxisblokk látható (válassza a weboldal jobb felső sarkában található Nyelv legördülő menüből a C++/WinRT lehetőséget). Figyelje meg a különböző konstruktorokat: az egyik egy lambdát használ; egy másik szabad függvényt; és egy másik (a fent használt) egy objektumot és egy mutató-tag függvényre használ.

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;
};

A függvényhívási operátor szintaxisa is hasznos lehet. Ez jelzi, 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.

Megjegyzés:

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;

Ez azt jelzi, hogy a UIElement.KeyDown esemény (a témakör, amelyen dolgozunk) egy olyan delegált típussal rendelkezik, amely KeyEventHandler, mivel ezt a típust kell megadni, amikor erre az eseménytípusra meghatalmazottat regisztrál. Ezért kövesse a témakör hivatkozását a KeyEventHandler delegált típusra,. 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::Windows::UI::Xaml::Input::KeyRoutedEventArgs const& e) const;

Amint látható, a meghatalmazottnak deklarálnia kell, hogy egy IInspectable legyen a feladó, és a KeyRoutedEventArgs osztály egy példánya argsként.

Egy másik példához tekintsük meg a Popup.Closed eseményt. Annak delegált típusa EventHandler<IInspectable>. A meghatalmazott tehát egy IInspectable lesz feladóként, és egy másik IInspectable (mivel ez az EventHandlertípusparamétere) az args.

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"));
    });
}

A meghatalmazott létrehozásakor dönthet úgy, hogy egy kicsit részletesebben fogalmaz. 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::Windows::UI::Xaml::Controls::Button>().Content(box_value(L"Clicked"));
    };
    myButton().Click(click_handler);
    AnotherButton().Click(click_handler);
}

Regisztrált meghatalmazott visszavonása

Meghatalmazott regisztrálásakor általában Önnek visszaadnak egy tokent. Ezt követően ezt a jogkivonatot használhatja a meghatalmazott visszavonásához; ez azt jelenti, hogy a meghatalmazott ki van regisztrálva az eseményből, és nem lesz meghívva, ha az esemény ismét előfordul.

Az egyszerűség kedvéért a fenti kód példák egyike sem mutatta meg, hogyan kell ezt megtenni. A következő példakódban azonban a jogkivonatot a struktúra privát adattagjában tárolja, és a destruktorban visszavonja annak kezelőjét.

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;
};

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).

Megjegyzés:

Ha egy eseményforrás szinkron módon generálja az eseményeket, törölheti a kezelőt, és biztos lehet abban, hogy nem fog több eseményt kapni. 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: Az mutató biztonságos elérése egy eseménykezelő képviselővel.

Másik lehetőségként, ha regisztrál egy delegáltat, megadhatja a winrt::auto_revoke értéket, amely a winrt::auto_revoke_ttípusú, esemény-visszavonóként szolgáló winrt::event_revokeresetében szükséges. Az esemény visszavonó gyenge hivatkozást tart az eseményforrásra (az eseményt kiváltó objektumra). Manuálisan is visszavonhatja az event_revoker::revoke tagfüggvényt; de az esemény-visszavonó automatikusan meghívja ezt a függvényt, amikor kikerül a hatókörből. A visszavonja függvény ellenőrzi, hogy az eseményforrás még létezik-e, és ha igen, akkor 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::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;
};

Az alábbiakban a dokumentációs téma szintaxisblokkjából vett részlet a ButtonBase::Click eseményhez. 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-visszavonót kell deklarálnia a harmadik túlterhelésből. Ugyanezeket a delegáltakat átadhatja a regisztráló és a event_revoker túlterheléssel.

// 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;

Megjegyzés:

A fenti Button::Click_revoker kód példában a típus aliasa a következőhöz winrt::event_revoker<winrt::Windows::UI::Xaml::Controls::Primitives::IButtonBase>: . Hasonló minta vonatkozik az összes C++/WinRT-eseményre. Minden Windows-futtatókörnyezeti esemény rendelkezik egy visszavonási függvény túlterhelésével, amely egy eseményvisszafedőt ad vissza, és a visszavonó típusa az eseményforrás tagja. Ezért, hogy egy másik példát vegyünk, a CoreWindow::SizeChanged eseményhez tartozik egy olyan regisztrációs függvény túlterhelt verziója, amely egy CoreWindow::SizeChanged_revokertípusú értéket ad vissza.

Lapnavigációs forgatókönyv esetén megfontolhatja a kezelők visszavonását. Ha többször navigál egy oldalra, majd visszalép, akkor érdemes lehet visszavonni az esetleges kezelőket, amikor eltávozik az oldalról. Esetleg, ha ugyanazt az oldalpéldányt újrafelhasználja, ellenőrizze a token értékét, és csak akkor regisztráljon, ha 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 az oldalon. A jelen témakör későbbi részében ismertetett negyedik lehetőség pedig az, hogy erős vagy gyenge hivatkozást rögzít a lambda függvény erre a objektumra.

Ha az automatikus visszavonási meghatalmazott nem regisztrál

Ha próbálja megadni a winrt::auto_revoke értéket egy delegált regisztrálásakor, és az eredmény egy winrt::hresult_no_interface kivétel, akkor ez általában azt jelenti, hogy az eseményforrás nem támogatja a gyenge referenciákat. Ez gyakori helyzet például a Windows.UI.Composition névtérben. Ebben az esetben nem használhatja az automatikus visszavonás funkciót. Vissza kell térnie az eseménykezelők manuális visszavonásához.

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 tevékenységek és műveletek (előrehaladással és anélkül) befejezési és/vagy előrehaladási eseményekkel rendelkeznek, amelyek a megfelelő típusú delegáltakat várják. Például egy haladást megvalósító aszinkron művelet előrehaladási eseménye (amely az IAsyncOperationWithProgress-t implementálja) megkövetel egy AsyncOperationProgressHandlertípusú delegáltat. Íme egy kód példa egy ilyen típusú delegált létrehozására lambdafüggvény használatával. A példa azt is bemutatja, hogyan hozhat létre AsyncOperationWithProgressCompletedHandler meghatalmazottat.

#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 "korutin" megjegyzés is sugallja, ahelyett, hogy egy delegátumot használna az aszinkron tevékenységek és műveletek befejezett eseményeinél, valószínűleg természetesebbnek fogja találni a korutinok használatát. További részletekért és példakódokért lásd a Egyidejűség és aszinkron műveletek C++/WinRT használatával.

Megjegyzés:

Nem helyes, ha egy aszinkron akcióhoz vagy művelethez egynél több befejezéskezelőt implementálnak. A befejezett eseményhez lehet egy meghatalmazott, vagy megteheti a co_await műveletet. 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::Windows::UI::Xaml::Controls;

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

A mutató biztonságos elérése eseménykezelő delegáttal

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