Visão geral de eventos anexados (WPF .NET)

A linguagem XAML (Extensible Application Markup Language) define um componente de linguagem e um tipo de evento chamado evento anexado. Os eventos anexados podem ser usados para definir um novo evento roteado em uma classe que não seja de elemento e gerar esse evento em qualquer elemento da árvore. Para fazer isso, você deve registrar o evento anexado como um evento roteado e fornecer código de suporte específico que ofereça suporte à funcionalidade de evento anexado. Como os eventos anexados são registrados como eventos roteados, quando gerados em um elemento eles se propagam pela árvore de elementos.

Importante

A documentação do Guia da Área de Trabalho para .NET 7 e .NET 6 está em construção.

Pré-requisitos

O artigo pressupõe um conhecimento básico de eventos roteados do Windows Presentation Foundation (WPF) e que você leu Visão geral de eventos roteados e XAML no WPF. Para seguir os exemplos neste artigo, é útil se você estiver familiarizado com XAML e souber como escrever aplicativos WPF.

Sintaxe de evento anexado

Na sintaxe XAML, um evento anexado é especificado por seu nome de evento e seu tipo de proprietário, na forma de <owner type>.<event name>. Como o nome do evento é qualificado com o nome de seu tipo de proprietário, a sintaxe permite que o evento seja anexado a qualquer elemento que possa ser instanciado. Essa sintaxe também é aplicável a manipuladores de eventos roteados regulares que se anexam a um elemento arbitrário ao longo da rota de eventos.

A sintaxe de atributo XAML a seguir anexa o AquariumFilter_Clean manipulador do evento anexado AquariumFilter.Cleanaquarium1 ao elemento :

<aqua:Aquarium x:Name="aquarium1" Height="300" Width="400" aqua:AquariumFilter.Clean="AquariumFilter_Clean"/>

Neste exemplo, o prefixo aqua: é necessário porque as AquariumFilter classes e existem em um namespace e Aquarium assembly CLR (Common Language Runtime) diferentes.

Você também pode anexar manipuladores para eventos anexados em code-behind. Para fazer isso, chame o AddHandler método no objeto ao qual o manipulador deve anexar e passe o identificador de evento e o manipulador como parâmetros para o método.

Como o WPF implementa eventos anexados

Os eventos anexados do WPF são implementados como eventos roteados apoiados por um RoutedEvent campo. Como resultado, os eventos anexados se propagam pela árvore de elementos depois de serem gerados. Geralmente, o objeto que gera o evento anexado, conhecido como a origem do evento, é uma fonte de sistema ou serviço. As fontes de sistema ou serviço não são uma parte direta da árvore de elementos. Para outros eventos anexados, a origem do evento pode ser um elemento na árvore, como um componente dentro de um controle composto.

Cenários de eventos anexados

No WPF, os eventos anexados são usados em determinadas áreas de recursos em que há uma abstração de nível de serviço. Por exemplo, o WPF faz uso de eventos anexados habilitados pelas classes estáticas Mouse ou Validation . As classes que interagem ou usam um serviço podem interagir com um evento usando a sintaxe de evento anexado ou expor o evento anexado como um evento roteado. A última opção faz parte de como uma classe pode integrar os recursos do serviço.

O sistema de entrada WPF usa eventos anexados extensivamente. No entanto, quase todos esses eventos anexados são apresentados como eventos roteados não anexados equivalentes por meio de elementos base. Cada evento de entrada roteado é um membro da classe de elemento base e é apoiado com um evento CLR "wrapper". Você raramente usará ou manipulará eventos anexados diretamente. Por exemplo, é mais fácil manipular o evento anexado Mouse.MouseDown subjacente em um UIElement evento roteado equivalente UIElement.MouseDown do que usando a sintaxe de evento anexado em XAML ou code-behind.

Os eventos anexados servem a um propósito de arquitetura, permitindo a expansão futura dos dispositivos de entrada. Por exemplo, um novo dispositivo de entrada só precisaria aumentar Mouse.MouseDown para simular a entrada do mouse, e não precisaria derivar de Mouse para fazer isso. Esse cenário envolve a manipulação de código do evento, já que a manipulação XAML do evento anexado não seria relevante.

Manipular um evento anexado

O processo para codificar e manipular um evento anexado é basicamente o mesmo que para um evento roteado não anexado.

Como observado anteriormente, os eventos anexados ao WPF existentes normalmente não se destinam a ser manipulados diretamente no WPF. Mais frequentemente, o objetivo de um evento anexado é permitir que um elemento dentro de um controle composto relate seu estado a um elemento pai dentro do controle. Nesse cenário, o evento é gerado no código e depende da manipulação de classe na classe pai relevante. Por exemplo, espera-se que os itens dentro de um Selector gerem o Selected evento anexado, que é então a classe manipulada Selector pela classe. A Selector classe potencialmente converte o Selected evento no SelectionChanged evento roteado. Para obter mais informações sobre eventos roteados e manipulação de classe, consulte Marcando eventos roteados como manipulados e Tratamento de classe.

Definir um evento anexado personalizado

Se você estiver derivando de classes base comuns do WPF, poderá implementar seu evento anexado personalizado incluindo dois métodos de acessador em sua classe. Esses métodos são:

  • Um método Add<event name>Handler , com um primeiro parâmetro que é o elemento no qual o manipulador de eventos está anexado e um segundo parâmetro que é o manipulador de eventos a ser adicionado. O método deve ser public e static, sem valor de retorno. O método chama o método de classe base, passando o evento roteado e o AddHandler manipulador como argumentos. Esse método dá suporte à sintaxe do atributo XAML para anexar um manipulador de eventos a um elemento. Esse método também habilita o acesso de código ao armazenamento do manipulador de eventos para o evento anexado.

  • Um método Remove<event name>Handler , com um primeiro parâmetro que é o elemento no qual o manipulador de eventos está anexado e um segundo parâmetro que é o manipulador de eventos a ser removido. O método deve ser public e static, sem valor de retorno. O método chama o método de classe base, passando o evento roteado e o RemoveHandler manipulador como argumentos. Esse método habilita o acesso de código ao repositório do manipulador de eventos para o evento anexado.

O WPF implementa eventos anexados como eventos roteados porque o identificador de um RoutedEvent é definido pelo sistema de eventos do WPF. Além disso, o roteamento de um evento é uma extensão natural do conceito de nível de linguagem XAML de um evento anexado. Essa estratégia de implementação restringe o tratamento de eventos anexados a UIElement classes derivadas ou ContentElement classes derivadas, porque somente essas classes têm AddHandler implementações.

Por exemplo, o código a seguir define o evento anexado CleanAquariumFilter na classe owner, que não é uma classe de elemento. O código define o evento anexado como um evento roteado e implementa os métodos de acessador necessários.

public class AquariumFilter
{
    // Register a custom routed event using the bubble routing strategy.
    public static readonly RoutedEvent CleanEvent = EventManager.RegisterRoutedEvent(
        "Clean", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(AquariumFilter));

    // Provide an add handler accessor method for the Clean event.
    public static void AddCleanHandler(DependencyObject dependencyObject, RoutedEventHandler handler)
    {
        if (dependencyObject is not UIElement uiElement)
            return;

        uiElement.AddHandler(CleanEvent, handler);
    }

    // Provide a remove handler accessor method for the Clean event.
    public static void RemoveCleanHandler(DependencyObject dependencyObject, RoutedEventHandler handler)
    {
        if (dependencyObject is not UIElement uiElement)
            return;

        uiElement.RemoveHandler(CleanEvent, handler);
    }
}
Public Class AquariumFilter

    ' Register a custom routed event using the bubble routing strategy.
    Public Shared ReadOnly CleanEvent As RoutedEvent = EventManager.RegisterRoutedEvent(
        "Clean", RoutingStrategy.Bubble, GetType(RoutedEventHandler), GetType(AquariumFilter))

    ' Provide an add handler accessor method for the Clean event.
    Public Shared Sub AddCleanHandler(dependencyObject As DependencyObject, handler As RoutedEventHandler)
        Dim uiElement As UIElement = TryCast(dependencyObject, UIElement)

        If uiElement IsNot Nothing Then
            uiElement.[AddHandler](CleanEvent, handler)
        End If
    End Sub

    ' Provide a remove handler accessor method for the Clean event.
    Public Shared Sub RemoveCleanHandler(dependencyObject As DependencyObject, handler As RoutedEventHandler)
        Dim uiElement As UIElement = TryCast(dependencyObject, UIElement)

        If uiElement IsNot Nothing Then
            uiElement.[RemoveHandler](CleanEvent, handler)
        End If
    End Sub

End Class

O RegisterRoutedEvent método que retorna o identificador de evento anexado é o mesmo método usado para registrar eventos roteados não anexados. Os eventos roteados anexados e não anexados são registrados em um armazenamento interno centralizado. Essa implementação de repositório de eventos habilita o conceito de "eventos como uma interface" discutido em Visão geral de eventos roteados.

Ao contrário do "wrapper" de eventos CLR usado para fazer backup de eventos roteados não anexados, os métodos de acessador de eventos anexados podem ser implementados em classes que não derivam de UIElement ou ContentElement. Isso é possível porque o código de suporte de evento anexado chama os UIElement.AddHandler métodos e UIElement.RemoveHandler em uma instância passada UIElement . Por outro lado, o wrapper CLR para eventos roteados não anexados chama esses métodos diretamente na classe proprietária, de modo que a classe deve derivar de UIElement.

Gerar um evento anexado do WPF

O processo para gerar um evento anexado é essencialmente o mesmo que para um evento roteado não anexado.

Normalmente, seu código não precisará gerar nenhum evento anexado definido pelo WPF existente, pois esses eventos seguem o modelo conceitual geral de "serviço". Nesse modelo, classes de serviço, como InputManager, são responsáveis por gerar eventos anexados definidos pelo WPF.

Ao definir um evento anexado personalizado usando o modelo WPF de basear eventos anexados em eventos roteados, use o método para gerar um evento anexado UIElement.RaiseEvent em qualquer UIElement ou ContentElement. Ao gerar um evento roteado, esteja ele anexado ou não, é necessário designar um elemento na árvore de elementos como a origem do evento. Essa fonte é então relatada como o RaiseEvent interlocutor. Por exemplo, para gerar o evento roteado anexado AquariumFilter.Clean em aquarium1:

aquarium1.RaiseEvent(new RoutedEventArgs(AquariumFilter.CleanEvent));
aquarium1.[RaiseEvent](New RoutedEventArgs(AquariumFilter.CleanEvent))

No exemplo anterior, aquarium1 é a origem do evento.

Confira também