Poznámka:
Přístup k této stránce vyžaduje autorizaci. Můžete se zkusit přihlásit nebo změnit adresáře.
Přístup k této stránce vyžaduje autorizaci. Můžete zkusit změnit adresáře.
Toto téma ukazuje, jak zaregistrovat a odregistrovat delegáty pro zpracování událostí pomocí C++/WinRT. Událost můžete zpracovat pomocí libovolného standardního objektu podobného funkci jazyka C++.
Poznámka:
Informace o instalaci a používání rozšíření C++/WinRT Visual Studio (VSIX) a balíčku NuGet (které společně poskytují podporu šablony projektu a sestavení), najdete v tématu podpora sady Visual Studio pro C++/WinRT.
Přidání obslužné rutiny události pomocí sady Visual Studio
Pohodlný způsob přidání obslužné rutiny události do projektu je pomocí uživatelského rozhraní Návrháře XAML v sadě Visual Studio. Když máte otevřenou stránku XAML v Návrháři XAML, vyberte ovládací prvek, jehož událost chcete zpracovat. Na stránce vlastností tohoto ovládacího prvku klikněte na ikonu blesku a zobrazte seznam všech událostí, které jsou zdrojem tohoto ovládacího prvku. Potom dvojklikněte na událost, kterou chcete zpracovat; například OnClicked.
Návrhář XAML přidá do zdrojových souborů prototyp události obslužné rutiny (a základní implementaci), abyste ji mohli nahradit vlastní implementací.
Poznámka:
Obslužné rutiny událostí obvykle nemusí být popsané v souboru Midl (.idl). Návrhář XAML tedy nepřidá prototypy obslužných funkcí událostí do souboru MIDL. Přidá pouze vaše soubory .h a .cpp.
Registrace delegáta pro zpracování události
Jednoduchým příkladem je zpracování události kliknutí na tlačítko. Obvykle se používá kód XAML k registraci členské funkce pro zpracování události, jako je tato.
// 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"));
}
Výše uvedený kód pochází z projektu Prázdná aplikace (C++/WinRT) v sadě Visual Studio. Kód myButton() volá vygenerovanou funkci příslušenství, která vrací Button, které jsme pojmenovali myButton. Pokud změníte x:Name prvek Button , změní se také název vygenerované funkce přístupového objektu.
Poznámka:
V tomto případě je zdrojem události (objekt, který vyvolá událost) tlačítkos názvem myButton. A příjemce události (objekt zpracovávající událost) je instance MainPage. Další informace najdete dále v tomto tématu o správě životnosti zdrojů událostí a příjemců událostí.
Místo deklarativního zpracování v markupu můžete imperativně zaregistrovat členskou funkci pro obsluhu události. Nemusí to být zřejmé z níže uvedeného příkladu kódu, ale argument volání ButtonBase::Click je instancí delegáta RoutedEventHandler. V tomto případě používáme přetížení konstruktoru RoutedEventHandler, které přebírá objekt a ukazatel na členskou funkci.
// MainPage.cpp
MainPage::MainPage()
{
InitializeComponent();
myButton().Click({ this, &MainPage::ClickHandler });
}
Důležité
Při registraci delegáta předá výše uvedený příklad kódu nezpracovaný tento ukazatel (odkazující na aktuální objekt). Informace o vytvoření silného nebo slabého odkazu na aktuální objekt najdete v tématu Při použití členské funkce jako delegáta.
Tady je příklad, který používá statickou členskou funkci; poznamenejte si jednodušší syntaxi.
// 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 */) { ... }
Existují další způsoby konstrukce RoutedEventHandler. Níže je blok syntaxe převzatý z tématu dokumentace pro RoutedEventHandler (v rozevíracím seznamu Jazyk v pravém horním rohu webové stránky zvolte C++/WinRT). Všimněte si různých konstruktorů: jeden přebírá lambda funkci; další volnou funkci; a další (ten, který jsme použili výše) přebírá objekt a ukazatel na členskou funkci.
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;
};
Syntaxe operátoru volání funkce je také užitečná pro zobrazení. Řekne vám, jaké parametry delegáta musí být. Jak vidíte, v tomto případě syntaxe operátoru volání funkce odpovídá parametrům naší MainPage::ClickHandler.
Poznámka:
Pokud chcete zjistit podrobnosti o delegátovi a parametry delegáta, přejděte nejprve do tématu dokumentace pro samotnou událost. Jako příklad si vezmeme událost
// Register
event_token KeyDown(KeyEventHandler const& handler) const;
Tyto informace nám říkají, že událost UIElement.KeyDown (téma, které používáme) má typ delegáta KeyEventHandler, protože to je typ, který předáte při registraci delegáta s tímto typem události. Takže teď postupujte podle odkazu na téma, na který delegát KeyEventHandler typ. Blok syntaxe zde obsahuje operátor volání funkce. A jak už jsme zmínili výše, dozvíte se, jaké parametry musí mít váš delegát.
void operator()(
winrt::Windows::Foundation::IInspectable const& sender,
winrt::Windows::UI::Xaml::Input::KeyRoutedEventArgs const& e) const;
Jak vidíte, delegát musí být deklarován tak, aby jako odesílatele převzal IInspectable a instanci třídy KeyRoutedEventArgs jako args.
Abychom se podívali na další příklad, prozkoumejme událost Popup.Closed. Jeho typ delegáta je EventHandler<IInspectable>. Váš delegát tedy vezme jako odesílatele IInspectable a další IInspectable (protože to je parametr typu EventHandleru) jako argument.
Pokud v obslužné rutině události nemáte moc práce, můžete místo členské funkce použít funkci lambda. Z níže uvedeného příkladu kódu to nemusí být zřejmé, ale delegát RoutedEventHandler je vytvořen z funkce lambda, která musí znovu odpovídat syntaxi operátoru volání funkce, který jsme probrali výše.
MainPage::MainPage()
{
InitializeComponent();
myButton().Click([this](IInspectable const& /* sender */, RoutedEventArgs const& /* args */)
{
myButton().Content(box_value(L"Clicked"));
});
}
Při vytváření delegáta můžete být o něco explicitnější. Pokud ho například chcete předat nebo ho použít vícekrát.
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);
}
Odvolání registrovaného delegáta
Když zaregistrujete delegáta, obvykle se vám vrátí token. Tento token můžete následně použít k odvolání svého delegáta; to znamená, že delegát je z události zrušený a nebude volán, pokud bude událost znovu vyvolána.
Kvůli jednoduchosti žádný z výše uvedených příkladů kódu neukazil, jak to udělat. Tento další příklad kódu však uloží token do soukromého datového členu struktury a odvolá jeho obslužnou rutinu v destruktoru.
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;
};
Místo silného odkazu, jako v příkladu výše, můžete uložit slabý odkaz na tlačítko (viz Silné a slabé odkazy v C++/WinRT).
Poznámka:
Když zdroj událostí vyvolá události synchronně, můžete zrušit váš obslužný program a mít jistotu, že nebudete dostávat žádné další události. Ale u asynchronních událostí, a to i po odvolání (a zejména při odvolání uvnitř destruktoru), může událost v letu dosáhnout objektu po zahájení destrukce. Vyhledání místa pro odhlášení odběru před zničením může tento problém zmírnit. Pro robustní řešení se podívejte na bezpečný přístup k tomuto ukazateli pomocí delegáta pro zpracování událostí.
Případně můžete při registraci delegáta zadat winrt::auto_revoke (což je hodnota typu winrt::auto_revoke_t) a požádat o odvolání události (typu winrt::event_revoker). Revokátor události drží slabý odkaz na zdroj události (objekt, který vyvolá událost) jménem vás. Můžete ručně odvolat voláním členské funkce event_revoker::revoke, ale objekt event revoker volá tuto funkci automaticky, když vyjde z dosahu. Funkce odvolání zkontroluje, zda zdroj události stále existuje, a pokud ano, odvolá váš delegát. V tomto příkladu není nutné ukládat zdroj událostí a nepotřebujete destruktor.
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;
};
Níže je blok syntaxe převzatý z tématu dokumentace pro ButtonBase::Click událost. Zobrazuje tři různé funkce registrace a odvolání. Můžete jasně zjistit, jaký typ odvolávače událostí potřebujete deklarovat z třetí přetížené verze. A stejné druhy delegátů můžete předat jak pro registraci s, tak pro odvolání s přetížením event_revoker.
// 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;
Poznámka:
V příkladu kódu výše Button::Click_revoker je alias typu pro winrt::event_revoker<winrt::Windows::UI::Xaml::Controls::Primitives::IButtonBase>. Podobný vzor platí pro všechny události C++/WinRT. Každá událost prostředí Windows Runtime má přetížení funkce odvolání, které vrací odvolávání událostí a typ odvolacího programu je členem zdroje událostí. Abychom uvedli další příklad, událost CoreWindow::SizeChanged má přetíženou funkci pro registraci, která vrací hodnotu typu CoreWindow::SizeChanged_revoker.
V případě navigace na stránce můžete zvážit odvolání obslužných rutin. Pokud opakovaně přecházíte na stránku a pak se vrátíte zpět, můžete zrušit všechny obslužné rutiny, když ze stránky odejdete. Případně pokud používáte stejnou instanci stránky, zkontrolujte hodnotu tokenu a zaregistrujte ji jenom v případě, že ještě není nastavená (if (!m_token){ ... }). Třetí možností je uložit rušitel události na stránku jako datovou proměnnou. A čtvrtou možností, jak je popsáno dále v tomto tématu, je zachycení silného nebo slabého odkazu na tento objekt ve funkci lambda.
Pokud se vašemu delegátu automatického odvolání nepodaří zaregistrovat
Pokud se pokusíte zadat winrt::auto_revoke při registraci delegáta a výsledkem je winrt::hresult_no_interface výjimka, obvykle to znamená, že zdroj události nepodporuje slabé odkazy. To je například běžná situace v jmenném prostoru Windows.UI.Composition. V takovém případě nemůžete použít funkci automatického odvolání. Budete se muset vrátit k ručnímu odvolání obslužných rutin událostí.
Typy delegátů pro asynchronní akce a operace
Výše uvedené příklady používají typ delegáta RoutedEventHandler , ale existuje samozřejmě mnoho dalších typů delegátů. Například asynchronní akce a operace (s průběhem a bez pokroku) se dokončily a/nebo průběhové události, které očekávají delegáty odpovídajícího typu. Například událost postupu asynchronní operace (což je vše, co implementuje IAsyncOperationWithProgress) vyžaduje delegáta typu AsyncOperationProgressHandler. Tady je příklad kódu pro vytvoření delegáta tohoto typu pomocí funkce lambda. Příklad také ukazuje, jak vytvořit AsyncOperationWithProgressCompletedHandler delegát.
#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 };
}
Jak naznačuje výše uvedený komentář o korutinách, místo použití delegáta s dokončenými událostmi asynchronních akcí a operací zjistíte, že může být přirozenější používat korutiny. Podrobnosti a příklady kódu najdete v tématu Souběžnost a asynchronní operace s C++/WinRT.
Poznámka:
Není správné implementovat více než jednu obslužnou rutinu dokončení pro asynchronní akci nebo operaci. Můžete mít buď jednoho delegáta pro dokončený event, nebo to můžete co_await. Pokud máte obojí, druhý selže.
Pokud místo korutiny zůstanete u delegátů, můžete zvolit jednodušší syntaxi.
async_op_with_progress.Completed(
[](auto&& /*sender*/, AsyncStatus const /* args */)
{
// ...
});
Typy delegátů, které vracejí hodnotu
Některé typy delegátů musí samy vrátit hodnotu. Příkladem je ListViewItemToKeyHandler, která vrací řetězec. Tady je příklad vytvoření delegáta tohoto typu (všimněte si, že funkce lambda vrací hodnotu).
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";
});
}
Bezpečný přístup k ukazateli pomocí delegáta pro zpracování událostí
Pokud zpracováváte událost pomocí členské funkce objektu nebo z funkce lambda uvnitř členské funkce objektu, musíte se zamyslet nad relativní životností příjemce události (objekt zpracovávající událost) a zdrojem události (objektem, který vyvolává událost). Pro další informace a ukázky kódu, viz silné a slabé odkazy v jazyce C++/WinRT.
Důležitá rozhraní API
- winrt::auto_revoke_t struktura značek
- winrt::implements::get_weak funkce
- winrt::implements::get_strong funkce