Eventos personalizados e acessadores de evento em componentes do Windows Runtime
O suporte do .NET para componentes Windows Runtime facilita a declaração de componentes de eventos ocultando as diferenças entre o padrão de evento Plataforma Universal do Windows (UWP) e o padrão de evento .NET. No entanto, ao declarar acessadores de eventos personalizados em um componente Windows Runtime, você deve seguir o padrão usado na UWP.
Registro de eventos
Quando você se registra para manipular um evento na UWP, acessador add retorna um token. Para cancelar o registro, você passa esse token para o acessador remove. Isso significa que os acessadores add e remove de eventos da UWP têm assinaturas diferentes dos acessadores a que você está acostumado.
Felizmente, os compiladores do Visual Basic e do C# simplificam esse processo: quando você declara um evento com acessadores personalizados em um componente Windows Runtime, os compiladores usam automaticamente o padrão UWP. Por exemplo, você obtém um erro de compilador caso o acessador add não retorne um token. O .NET fornece dois tipos para dar suporte à implementação:
- A estrutura EventRegistrationToken representa o token.
- A classe EventRegistrationTokenTable<T> cria tokens e mantém um mapeamento entre tokens e manipuladores de eventos. O argumento de tipo genérico é o tipo de argumento do evento. Você cria uma instância dessa classe para cada evento na primeira vez em que um manipulador de eventos é registrado para esse evento.
O código a seguir do evento NumberChanged mostra o padrão básico de eventos UWP. Neste exemplo, o construtor do objeto de argumento do evento, NumberChangedEventArgs, utiliza um único parâmetro inteiro que representa o valor numérico alterado.
Nota Esse é o mesmo padrão que os compiladores usam para eventos comuns que você declara em um componente Windows Runtime.
private EventRegistrationTokenTable<EventHandler<NumberChangedEventArgs>>
m_NumberChangedTokenTable = null;
public event EventHandler<NumberChangedEventArgs> NumberChanged
{
add
{
return EventRegistrationTokenTable<EventHandler<NumberChangedEventArgs>>
.GetOrCreateEventRegistrationTokenTable(ref m_NumberChangedTokenTable)
.AddEventHandler(value);
}
remove
{
EventRegistrationTokenTable<EventHandler<NumberChangedEventArgs>>
.GetOrCreateEventRegistrationTokenTable(ref m_NumberChangedTokenTable)
.RemoveEventHandler(value);
}
}
internal void OnNumberChanged(int newValue)
{
EventHandler<NumberChangedEventArgs> temp =
EventRegistrationTokenTable<EventHandler<NumberChangedEventArgs>>
.GetOrCreateEventRegistrationTokenTable(ref m_NumberChangedTokenTable)
.InvocationList;
if (temp != null)
{
temp(this, new NumberChangedEventArgs(newValue));
}
}
Private m_NumberChangedTokenTable As _
EventRegistrationTokenTable(Of EventHandler(Of NumberChangedEventArgs))
Public Custom Event NumberChanged As EventHandler(Of NumberChangedEventArgs)
AddHandler(ByVal handler As EventHandler(Of NumberChangedEventArgs))
Return EventRegistrationTokenTable(Of EventHandler(Of NumberChangedEventArgs)).
GetOrCreateEventRegistrationTokenTable(m_NumberChangedTokenTable).
AddEventHandler(handler)
End AddHandler
RemoveHandler(ByVal token As EventRegistrationToken)
EventRegistrationTokenTable(Of EventHandler(Of NumberChangedEventArgs)).
GetOrCreateEventRegistrationTokenTable(m_NumberChangedTokenTable).
RemoveEventHandler(token)
End RemoveHandler
RaiseEvent(ByVal sender As Class1, ByVal args As NumberChangedEventArgs)
Dim temp As EventHandler(Of NumberChangedEventArgs) = _
EventRegistrationTokenTable(Of EventHandler(Of NumberChangedEventArgs)).
GetOrCreateEventRegistrationTokenTable(m_NumberChangedTokenTable).
InvocationList
If temp IsNot Nothing Then
temp(sender, args)
End If
End RaiseEvent
End Event
O método Estático (Compartilhado no Visual Basic) GetOrCreateEventRegistrationTokenTable cria a instância do evento do objeto EventRegistrationTokenTable<T> lentamente. Passe o campo de nível de classe que armazenará a instância da tabela de tokens para esse método. Caso o campo esteja vazio, o método cria a tabela, armazena uma referência à tabela no campo e retorna uma referência para a tabela. Caso o campo já contenha uma referência à tabela de tokens, o método retorna apenas essa referência.
Importante Para garantir a segurança do thread, o campo que contém a instância do evento eventRegistrationTokenTable<T> deve ser um campo de nível de classe. Caso ele seja um campo de nível de classe, o método GetOrCreateEventRegistrationTokenTable garante que, quando vários threads tentam criar a tabela de tokens, todos os threads obtêm a mesma instância da tabela. Para um determinado evento, todas as chamadas para o método GetOrCreateEventRegistrationTokenTable devem usar o mesmo campo de nível de classe.
Chamar o método GetOrCreateEventRegistrationTokenTable no acessador remove e no método RaiseEvent (o método OnRaiseEvent em C#) garante que nenhuma exceção ocorra caso esses métodos sejam chamados antes de qualquer representante do manipulador de eventos ter sido adicionado.
Entre os outros membros da classe EventRegistrationTokenTable<T> que são usados no padrão de evento UWP estão os seguintes:
O método AddEventHandler gera um token para o representante do manipulador de eventos, armazena o representante na tabela, adiciona-o à lista de invocações e devolve o token.
A sobrecarga de método RemoveEventHandler(EventRegistrationToken) remove o representante da tabela e da lista de invocações.
Nota Os métodos AddEventHandler e RemoveEventHandler(EventRegistrationToken) bloqueiam a tabela para ajudar a garantir a segurança do thread.
A propriedade InvocationList retorna um representante que inclui todos os manipuladores de eventos registrados no momento para manipular o evento. Use esse representante para acionar o evento ou use os métodos da classe Delegate para invocar os manipuladores individualmente.
Nota Recomendamos que você siga o padrão mostrado no exemplo fornecido anteriormente neste artigo e copie o delegado para uma variável temporária antes de invocá-lo. Isso evita uma condição de corrida em que um thread remove o último manipulador, o que reduz o representante para nulo antes de outro thread tentar invocar o representante. Como os representantes são imutáveis, a cópia continua sendo válida.
Coloque o próprio código nos acessadores conforme apropriado. Caso a segurança do thread seja um problema, você deve fornecer o próprio bloqueio para o código.
Usuários de C#: quando você escreve acessadores de eventos personalizados no padrão de eventos da UWP, o compilador não fornece os atalhos sintáticos usuais. Ele gera erros caso você use o nome do evento no código.
Usuários do Visual Basic: no .NET, um evento é apenas um delegado multicast que representa todos os manipuladores de eventos registrados. Acionar o evento significa apenas invocar o representante. A sintaxe do Visual Basic normalmente oculta as interações com o representante, e o compilador copia o representante antes de invocá-lo, conforme descrito na observação sobre segurança do thread. Ao criar um evento personalizado em um componente Windows Runtime, você precisa lidar diretamente com o delegado. Isso também significa ser possível, por exemplo, usar o método MulticastDelegate.GetInvocationList para obter uma matriz que contém um representante separado para cada manipulador de eventos, caso você queira invocar os manipuladores separadamente.
Tópicos relacionados
Comentários
https://aka.ms/ContentUserFeedback.
Em breve: Ao longo de 2024, eliminaremos os problemas do GitHub como o mecanismo de comentários para conteúdo e o substituiremos por um novo sistema de comentários. Para obter mais informações, consulteEnviar e exibir comentários de