Eventos personalizados e acessadores de evento nos componentes do Tempo de Execução do Windows
O suporte do .NET Framework para o Tempo de Execução do Windows facilita declarar eventos em componentes do Tempo de Execução do Windows ocultando as diferenças entre o padrão de eventos do Tempo de Execução do Windows e o padrão de eventos do .NET Framework. No entanto, quando você declara acessadores de evento personalizados em um componente do Tempo de Execução do Windows, deve seguir o padrão do Tempo de Execução do Windows.
Quando você se registra para manipular um evento no Tempo de Execução do Windows, o acessador add retorna um token. Ao cancelar o registro, você transmite esse token para o acessador remove. Isso significa que os acessadores add e remove para eventos do Tempo de Execução do Windows têm assinaturas diferentes dos acessadores que você conhece.
Felizmente, os compiladores do Visual Basic e C# simplificam o processo: quando você declara um evento com acessadores personalizados em um componente do Tempo de Execução do Windows, os compiladores usam automaticamente o padrão do Tempo de Execução do Windows. Por exemplo, você obterá um erro de compilador se o seu acessador add não retornar um token. O .NET Framework fornece dois tipos para oferecer 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 de evento. Você cria uma instância dessa classe para cada evento na primeira vez que um manipulador de eventos é registrado para o evento.
O seguinte código para o evento NumberChanged mostra o padrão básico para eventos do Tempo de Execução do Windows. Nesse exemplo, o construtor para o objeto de argumento do evento, NumberChangedEventArgs, aceita um único parâmetro inteiro que representa o valor numérico alterado.
Dica
Esse é o mesmo padrão que os compiladores usam para os eventos comuns que você declara em um componente do Tempo de Execução do Windows.
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 GetOrCreateEventRegistrationTokenTable static (Shared no Visual Basic) cria a instância do evento do objeto EventRegistrationTokenTable<T> lentamente. Transmita o campo no nível de classe que conterá a instância de tabela de token para este método. Se o campo estiver vazio, o método criará a tabela, armazenará uma referência à tabela no campo e retornará uma referência para a tabela. Se o campo já contiver uma referência de tabela de token, o método retornará apenas essa referência.
Importante
Para garantir segurança de segmentos, o campo que contém a instância do evento EventRegistrationTokenTable<T> deve ser um campo no nível de classe. Se for um campo no nível de classe, o método GetOrCreateEventRegistrationTokenTable garantirá que quando vários segmentos tentarem criar a tabela de token, todos os segmentos obtenham a mesma instância da tabela. Para um determinado evento, todas as chamadas para o método GetOrCreateEventRegistrationTokenTable devem usar o mesmo campo no nível de classe.
Chamar o método GetOrCreateEventRegistrationTokenTable no acessador remove e o método RaiseEvent (o método OnRaiseEvent em C#) garante que nenhuma exceção ocorra se esses métodos forem chamados antes de todos os representantes do manipulador de eventos serem adicionados.
Os outros membros da classe EventRegistrationTokenTable<T> que são usados no padrão de eventos do Tempo de Execução do Windows incluem o seguinte:
O método AddEventHandler gera um token para o representante do manipulador de eventos, armazena o representante na tabela, adiciona-o à lista de invocação e retorna o token.
A sobrecarga do método RemoveEventHandler(EventRegistrationToken) remove o representante da tabela e da lista de invocação.
Dica
Os métodos AddEventHandler e RemoveEventHandler(EventRegistrationToken) bloqueiam a tabela para ajudar a garantir a segurança dos segmentos.
A propriedade InvocationList retorna um representante que inclui todos os manipuladores de eventos registrados no momento para manipular o evento. Use esse representante para gerar o evento, ou use os métodos da classe Delegate para invocar manipuladores individualmente.
Dica
Recomendamos que você siga o padrão mostrado no exemplo fornecido anteriormente neste artigo e copie o representante em uma variável temporária antes de chamá-lo. Isso evita uma condição de corrida em que o segmento remove o último manipulador, reduzindo o representante a null imediatamente antes que outro segmento tentar chamar o representante. Representantes são imutáveis, então, a cópia ainda é válida.
Coloque seu próprio código nos acessadores conforme apropriado. Se a segurança dos segmentos for um problema, você deve fornecer seu próprio bloqueio para seu código.
Usuários da linguagem C#: ao gravar acessadores de evento personalizados no padrão de eventos do Tempo de Execução do Windows, o compilador não fornece os atalhos sintáticos usuais. Ele gerará erros se você usar o nome do evento em seu código.
Usuários do Visual Basic: no .NET Framework, um evento é apenas um representante multicast que representa todos os manipuladores de eventos registrados. Disparar o evento significa apenas chamar o representante. Em geral, a sintaxe do Visual Basic oculta as interações com o representante, e o compilador copia o representante antes de chamá-lo, conforme descrito na nota sobre segurança de segmentos. Quando você cria um evento personalizado em um componente do Tempo de Execução do Windows, é necessário lidar com o representante diretamente. Isso também significa que você pode, por exemplo, usar o método MulticastDelegate.GetInvocationList para obter uma matriz que contém um representante separado para cada manipulador de eventos, se você desejar chamar manipuladores separadamente.
Consulte também
Tarefas
Como implementar acessadores de eventos personalizados (Guia de Programação em C#)
Referência
Eventos (Guia de Programação em C#)
Outros recursos
.NET Framework Support for Windows Store Apps and Windows Runtime