Observação
O acesso a essa página exige autorização. Você pode tentar entrar ou alterar diretórios.
O acesso a essa página exige autorização. Você pode tentar alterar os diretórios.
Important
Construindo com o SDK do Aplicativo Windows? O código deste artigo usa namespaces UWP (Windows.UI.Xaml). Se o seu projeto tiver como destino o WinUI 3 (SDK do Aplicativo Windows), substitua Microsoft.UI.Xaml (e os namespaces Microsoft.UI.* relacionados) em todo o texto. Consulte Mapeamento de APIs UWP para o SDK do Aplicativo Windows para ver um mapeamento completo e o guia de migração da interface do usuário para mais detalhes.
Este tópico mostra como registrar e revogar delegados de manipulação de eventos usando C++/WinRT. Você pode manipular um evento usando qualquer objeto de função C++ padrão.
Note
Para obter informações sobre como instalar e usar o VSIX (Extensão de Visual Studio) do C++/WinRT e o pacote NuGet (que juntos fornecem suporte ao modelo de projeto e ao build), consulte Visual Studio suporte para C++/WinRT.
Usando Visual Studio para adicionar um manipulador de eventos
Uma maneira conveniente de adicionar um manipulador de eventos ao seu projeto é usando a interface do usuário (interface do usuário) do Designer XAML em Visual Studio. Com a página XAML aberta no Designer XAML, selecione o controle cujo evento você deseja manipular. Na página de propriedades desse controle, clique no ícone de raio para listar todos os eventos originados por esse controle. Em seguida, clique duas vezes no evento que você deseja manipular; por exemplo, OnClicked.
O Designer do XAML adiciona o protótipo apropriado da função de manipulador de eventos (e uma implementação vazia) aos seus arquivos de origem, deixando-o pronto para que você o substitua pela sua própria implementação.
Note
Normalmente, os manipuladores de eventos não precisam ser descritos no arquivo Midl (.idl). Portanto, o Designer XAML não adiciona protótipos de função do manipulador de eventos ao arquivo Midl. Ele apenas adiciona seus arquivos .h e .cpp.
Registrar um delegado para manipular um evento
Um exemplo simples é lidar com o evento de clique de um botão. É comum usar a marcação XAML para registrar uma função membro para tratar o evento, da seguinte forma.
// 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"));
}
O código acima foi retirado do projeto Blank App, Packaged (WinUI 3 para Desktop) no Visual Studio. O código myButton() chama uma função acessora gerada, que retorna o Button ao qual demos o nome de myButton. Se você alterar o x:Name do elemento Button, o nome da função de acesso gerada também mudará.
Note
Nesse caso, a origem do evento (o objeto que aciona o evento) é o botão chamado myButton. E o destinatário do evento (o objeto que está tratando o evento) é uma instância do MainPage. Há mais informações posteriormente neste tópico sobre como gerenciar o tempo de vida de fontes de eventos e destinatários de eventos.
Em vez de fazer isso declarativamente na marcação, você pode registrar imperativamente uma função membro para lidar com um evento. Pode não ser óbvio no exemplo de código abaixo, mas o argumento para a chamada ButtonBase::Click é uma instância do delegado RoutedEventHandler . Nesse caso, estamos usando a sobrecarga do construtor RoutedEventHandler que recebe um objeto e um ponteiro para função membro.
// MainPage.cpp
MainPage::MainPage()
{
InitializeComponent();
myButton().Click({ this, &MainPage::ClickHandler });
}
Important
Ao registrar o delegado, o exemplo de código acima passa um ponteiro bruto this (que aponta para o objeto atual). Para saber como estabelecer uma referência forte ou fraca ao objeto atual, consulte Se você usa uma função de membro como delegado.
Aqui está um exemplo que usa uma função de membro estático; observe a sintaxe mais simples.
// 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 */) { ... }
Há outras maneiras de construir um RoutedEventHandler. Veja abaixo o bloco de sintaxe retirado do tópico de documentação para RoutedEventHandler (escolha C++/WinRT na lista suspensa Idioma no canto superior direito da página da Web). Observe os vários construtores: um usa um lambda; outra uma função gratuita; e outro (aquele que usamos acima) usa um objeto e uma função de ponteiro para membro.
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 sintaxe do operador de chamada de função também é útil para visualização. Ele informa quais devem ser os parâmetros do delegate. Como você pode ver, nesse caso, a sintaxe do operador de chamada de função corresponde aos parâmetros de nosso MainPage::ClickHandler.
Note
Para qualquer evento específico, para descobrir os detalhes de seu representante e os parâmetros desse delegado, vá primeiro para o tópico de documentação do próprio evento. Vamos usar o evento UIElement.KeyDown como exemplo. Visite esse tópico e escolha C++/WinRT no menu suspenso Idioma. No bloco de sintaxe no início do tópico, você verá isso.
// Register
event_token KeyDown(KeyEventHandler const& handler) const;
Essas informações nos dizem que o evento UIElement.KeyDown (o tópico que estamos abordando) tem um tipo de delegado KeyEventHandler, já que esse é o tipo que você passa ao registrar um delegado para esse tipo de evento. Então, agora siga o link no tópico até esse tipo KeyEventHandler delegate. Aqui, o bloco de sintaxe contém um operador de chamada de função. E, conforme mencionado acima, isso indica quais precisam ser os parâmetros do seu delegate.
void operator()(
winrt::Windows::Foundation::IInspectable const& sender,
winrt::Microsoft::UI::Xaml::Input::KeyRoutedEventArgs const& e) const;
Como você pode ver, o delegado precisa ser declarado para tomar um IInspectable como o remetente e uma instância da classe KeyRoutedEventArgs como os args.
Para dar outro exemplo, vamos examinar o evento Popup.Closed. Seu tipo delegado é EventHandler<IInspectable>. Portanto, seu delegado usará um IInspectable como remetente e outro IInspectable (porque esse é o parâmetro de tipo do EventHandler) como os args.
Se você não estiver fazendo muito trabalho no manipulador de eventos, poderá usar uma função lambda em vez de uma função de membro. Novamente, pode não ser óbvio no exemplo de código abaixo, mas um delegado RoutedEventHandler está sendo construído a partir de uma função lambda que, novamente, precisa corresponder à sintaxe do operador de chamada de função que discutimos acima.
MainPage::MainPage()
{
InitializeComponent();
myButton().Click([this](IInspectable const& /* sender */, RoutedEventArgs const& /* args */)
{
myButton().Content(box_value(L"Clicked"));
});
}
Você pode optar por ser um pouco mais explícito ao construir seu delegado. Por exemplo, se você quiser passá-lo adiante ou usá-lo mais de uma vez.
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);
}
Revogar um delegado registrado
Quando você registra um delegado, normalmente um token é retornado para você. Posteriormente, você pode usar esse token para revogar seu delegado; o que significa que o delegado não foi registrado do evento e não será chamado caso o evento seja acionado novamente.
Para simplificar, nenhum dos exemplos de código acima mostrou como fazer isso. Mas o próximo exemplo de código armazena o token no membro de dados privado da struct e revoga seu manipulador no destrutor.
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;
};
Em vez de uma referência forte, como no exemplo acima, você pode armazenar uma referência fraca ao botão (consulte referências fortes e fracas em C++/WinRT).
Note
Quando uma fonte de evento gera seus eventos de forma síncrona, você pode revogar seu manipulador e ter certeza de que não receberá mais eventos. No entanto, para eventos assíncronos, mesmo após a revogação (e especialmente ao revogar dentro do destruidor), um evento de pré-lançamento pode alcançar seu objeto depois de começar a se destruir. Encontrar um local para cancelar a assinatura antes da destruição pode atenuar o problema, ou, para uma solução robusta, consulte Acessando com segurança o ponteiro this com um delegado de tratamento de eventos.
Como alternativa, ao registrar um delegado, você pode especificar winrt::auto_revoke (que é um valor do tipo winrt::auto_revoke_t) para solicitar um revogador de evento (do tipo winrt::event_revoker). O revogador de eventos contém uma referência fraca à origem do evento (o objeto que gera o evento) para você. Você pode revogar manualmente chamando a função de membro event_revoker::revoke ; mas o revogador de eventos chama essa função automaticamente quando ela sai do escopo. A função revoke verifica se a origem do evento ainda existe e, em caso afirmativo, revoga seu delegado. Neste exemplo, não é necessário armazenar a origem do evento e não há necessidade de um destruidor.
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;
};
Veja abaixo o bloco de sintaxe retirado do tópico de documentação do evento ButtonBase::Click . Ele mostra as três funções de registro e revogação diferentes. Você pode ver exatamente que tipo de revogador de eventos você precisa declarar da terceira sobrecarga. E você pode passar os mesmos tipos de delegados tanto para as sobrecargas de register quanto para as de revoke with event_revoker.
// 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
No exemplo de código acima, Button::Click_revoker há um alias de tipo para winrt::event_revoker<winrt::Microsoft::UI::Xaml::Controls::Primitives::IButtonBase>. Um padrão semelhante se aplica a todos os eventos C++/WinRT. Cada evento do Windows Runtime tem uma sobrecarga da função de revogação que retorna um revogador de evento, e o tipo desse revogador é um membro da origem do evento. Portanto, para obter outro exemplo, o evento Window::SizeChanged tem uma sobrecarga de função de registro que retorna um valor do tipo Window::SizeChanged_revoker.
Você pode considerar a revogação de manipuladores em um cenário de navegação de página. Se você acessa a mesma página repetidamente e depois sai dela, pode remover quaisquer manipuladores ao sair da página. Como alternativa, se você estiver usando novamente a mesma instância de página, verifique o valor do token e registre-se apenas se ele ainda não tiver sido definido (if (!m_token){ ... }). Uma terceira opção é armazenar um revogador de eventos na página como um membro de dados. E uma quarta opção, conforme descrito posteriormente neste tópico, é capturar uma referência forte ou fraca para esse objeto em sua função lambda.
Se o delegado de revogação automática não for registrado
Se você tentar especificar winrt::auto_revoke ao registrar um delegado e o resultado for uma exceção winrt::hresult_no_interface , isso geralmente significa que a origem do evento não dá suporte a referências fracas. Essa é uma situação comum no namespace Microsoft.UI.Composition, por exemplo. Nessa situação, você não pode usar o recurso de revogação automática. Você precisará voltar a revogar manualmente seus manipuladores de eventos.
Delegar tipos para operações e ações assíncronas
Os exemplos acima usam o tipo de delegado RoutedEventHandler , mas há, naturalmente, muitos outros tipos de delegado. Por exemplo, ações e operações assíncronas (com e sem progresso) foram concluídas e/ou eventos de progresso que esperam representantes do tipo correspondente. Por exemplo, o evento de progresso de uma operação assíncrona com progresso (que é qualquer coisa que implementa IAsyncOperationWithProgress) requer um delegado do tipo AsyncOperationProgressHandler. Aqui está um exemplo de código de criação de um delegado desse tipo usando uma função lambda. O exemplo também mostra como criar um delegado AsyncOperationWithProgressCompletedHandler .
#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 };
}
Como o comentário "coroutine" acima sugere, em vez de usar um delegado com os eventos concluídos de ações e operações assíncronas, você provavelmente achará mais natural usar coroutines. Para obter detalhes e exemplos de código, consulte Simultaneidade e operações assíncronas com C++/WinRT.
Note
Não é correto implementar mais de um manipulador de conclusão para uma ação ou operação assíncrona. Você pode ter um único delegado para o evento concluído ou pode co_await . Se você tiver os dois, o segundo falhará.
Se você ficar com delegados em vez de uma coroutina, poderá optar por uma sintaxe mais simples.
async_op_with_progress.Completed(
[](auto&& /*sender*/, AsyncStatus const /* args */)
{
// ...
});
Tipos de delegado que retornam um valor
Alguns tipos delegados devem, eles próprios, retornar um valor. Um exemplo é ListViewItemToKeyHandler, que retorna uma cadeia de caracteres. Aqui está um exemplo de criação de um delegado desse tipo (observe que a função lambda retorna um valor).
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";
});
}
Acessando com segurança o ponteiro this com um delegado de manipulação de eventos
Se você tratar um evento usando a função membro de um objeto, ou de dentro de uma função lambda dentro da função membro de um objeto, precisará considerar os tempos de vida relativos do destinatário do evento (o objeto que trata o evento) e da fonte do evento (o objeto que gera o evento). Para obter mais informações e exemplos de código, consulte referências fortes e fracas em C++/WinRT.
APIs importantes
- estrutura marcadora winrt::auto_revoke_t
- Função winrt::implements::get_weak
- Função winrt::implements::get_strong
Tópicos relacionados:
Windows developer