Como criar um evento roteado personalizado (WPF .NET)

Os desenvolvedores de aplicativos do Windows Presentation Foundation (WPF) e os autores de componentes podem criar eventos roteados personalizados para estender a funcionalidade de eventos CLR (Common Language Runtime). Para obter informações sobre recursos de eventos roteados, consulte Por que usar eventos roteados. Este artigo aborda os conceitos básicos da criação de um evento roteado personalizado.

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 e que você leu Visão geral de eventos roteados. Para seguir os exemplos neste artigo, é útil se você estiver familiarizado com XAML (Extensible Application Markup Language) e souber como escrever aplicativos Windows Presentation Foundation (WPF).

Etapas do evento roteado

As etapas básicas para criar um evento roteado são:

  1. Registre um RoutedEvent usando o RegisterRoutedEvent método.

  2. A chamada de registro retorna uma RoutedEvent instância, conhecida como um identificador de evento roteado, que contém o nome do evento registrado, a estratégia de roteamento e outros detalhes do evento. Atribua o identificador a um campo somente leitura estático. Por convenção:

    • O identificador de um evento roteado com uma estratégia borbulhante é chamado <event name>Event. Por exemplo, se o nome do evento for Tap , o identificador deverá ser nomeado TapEvent.
    • O identificador de um evento roteado com uma estratégia de encapsulamento é denominado Preview<event name>Event. Por exemplo, se o nome do evento for Tap , o identificador deverá ser nomeado PreviewTapEvent.
  3. Defina CLR adicionar e remover acessadores de eventos. Sem os acessadores de eventos CLR, você só poderá adicionar ou remover manipuladores de eventos por meio de chamadas diretas para os UIElement.AddHandler métodos e UIElement.RemoveHandler . Com os acessadores de eventos CLR, você obtém estes mecanismos de atribuição do manipulador de eventos:

    • Para XAML (Extensible Application Markup Language), você pode usar a sintaxe de atributo para adicionar manipuladores de eventos.
    • Para C#, você pode usar os += operadores e -= para adicionar ou remover manipuladores de eventos.
    • Para VB, você pode usar as instruções AddHandler e RemoveHandler para adicionar ou remover manipuladores de eventos.
  4. Adicione lógica personalizada para disparar o evento roteado. Por exemplo, sua lógica pode disparar o evento com base na entrada do usuário e no estado do aplicativo.

Exemplo

O exemplo a seguir implementa a CustomButton classe em uma biblioteca de controle personalizada. A CustomButton classe, que deriva de Button:

  1. Registra um RoutedEvent nome ConditionalClick usando o método e especifica a estratégia de borbulhamento durante o RegisterRoutedEventregistro.
  2. Atribui a instância retornada da chamada de registro a RoutedEvent um campo somente leitura estático chamado ConditionalClickEvent.
  3. Define CLR adicionar e remover acessadores de eventos.
  4. Adiciona lógica personalizada para gerar o evento roteado personalizado quando o é clicado CustomButton e uma condição externa se aplica. Embora o código de exemplo gere o ConditionalClick evento roteado de dentro do método virtual substituído OnClick , você pode gerar seu evento da maneira que quiser.
public class CustomButton : Button
{
    // Register a custom routed event using the Bubble routing strategy.
    public static readonly RoutedEvent ConditionalClickEvent = EventManager.RegisterRoutedEvent(
        name: "ConditionalClick",
        routingStrategy: RoutingStrategy.Bubble,
        handlerType: typeof(RoutedEventHandler),
        ownerType: typeof(CustomButton));

    // Provide CLR accessors for assigning an event handler.
    public event RoutedEventHandler ConditionalClick
    {
        add { AddHandler(ConditionalClickEvent, value); }
        remove { RemoveHandler(ConditionalClickEvent, value); }
    }

    void RaiseCustomRoutedEvent()
    {
        // Create a RoutedEventArgs instance.
        RoutedEventArgs routedEventArgs = new(routedEvent: ConditionalClickEvent);

        // Raise the event, which will bubble up through the element tree.
        RaiseEvent(routedEventArgs);
    }

    // For demo purposes, we use the Click event as a trigger.
    protected override void OnClick()
    {
        // Some condition combined with the Click event will trigger the ConditionalClick event.
        if (DateTime.Now > new DateTime())
            RaiseCustomRoutedEvent();

        // Call the base class OnClick() method so Click event subscribers are notified.
        base.OnClick();
    }
}
Public Class CustomButton
    Inherits Button

    ' Register a custom routed event with the Bubble routing strategy.
    Public Shared ReadOnly ConditionalClickEvent As RoutedEvent = EventManager.RegisterRoutedEvent(
        name:="ConditionalClick",
        routingStrategy:=RoutingStrategy.Bubble,
        handlerType:=GetType(RoutedEventHandler),
        ownerType:=GetType(CustomButton))

    ' Provide CLR accessors to support event handler assignment.
    Public Custom Event ConditionalClick As RoutedEventHandler

        AddHandler(value As RoutedEventHandler)
            [AddHandler](ConditionalClickEvent, value)
        End AddHandler

        RemoveHandler(value As RoutedEventHandler)
            [RemoveHandler](ConditionalClickEvent, value)
        End RemoveHandler

        RaiseEvent(sender As Object, e As RoutedEventArgs)
            [RaiseEvent](e)
        End RaiseEvent

    End Event

    Private Sub RaiseCustomRoutedEvent()

        ' Create a RoutedEventArgs instance.
        Dim routedEventArgs As New RoutedEventArgs(routedEvent:=ConditionalClickEvent)

        ' Raise the event, which will bubble up through the element tree.
        [RaiseEvent](routedEventArgs)

    End Sub

    ' For demo purposes, we use the Click event as a trigger.
    Protected Overrides Sub OnClick()

        ' Some condition combined with the Click event will trigger the ConditionalClick event.
        If Date.Now > New DateTime() Then RaiseCustomRoutedEvent()

        ' Call the base class OnClick() method so Click event subscribers are notified.
        MyBase.OnClick()

    End Sub
End Class

O exemplo inclui um aplicativo WPF separado que usa marcação XAML para adicionar uma instância do CustomButton a a e StackPanelatribuir o método como o Handler_ConditionalClickConditionalClick manipulador de eventos para os CustomButton elementos e StackPanel1 .

<Window x:Class="CodeSample.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:custom="clr-namespace:WpfControl;assembly=WpfControlLibrary"
        Title="How to create a custom routed event" Height="100" Width="300">

    <StackPanel Name="StackPanel1" custom:CustomButton.ConditionalClick="Handler_ConditionalClick">
        <custom:CustomButton
            Name="customButton"
            ConditionalClick="Handler_ConditionalClick"
            Content="Click to trigger a custom routed event"
            Background="LightGray">
        </custom:CustomButton>
    </StackPanel>
</Window>

No code-behind, o aplicativo WPF define o Handler_ConditionalClick método do manipulador de eventos. Os métodos de manipulador de eventos só podem ser implementados em code-behind.

// The ConditionalClick event handler.
private void Handler_ConditionalClick(object sender, RoutedEventArgs e)
{
    string senderName = ((FrameworkElement)sender).Name;
    string sourceName = ((FrameworkElement)e.Source).Name;

    Debug.WriteLine($"Routed event handler attached to {senderName}, " +
        $"triggered by the ConditionalClick routed event raised on {sourceName}.");
}

// Debug output when CustomButton is clicked:
// Routed event handler attached to CustomButton,
//     triggered by the ConditionalClick routed event raised on CustomButton.
// Routed event handler attached to StackPanel1,
//     triggered by the ConditionalClick routed event raised on CustomButton.
' The ConditionalClick event handler.
Private Sub Handler_ConditionalClick(sender As Object, e As RoutedEventArgs)

    Dim sourceName As String = CType(e.Source, FrameworkElement).Name
    Dim senderName As String = CType(sender, FrameworkElement).Name

    Debug.WriteLine($"Routed event handler attached to {senderName}, " +
        $"triggered by the ConditionalClick routed event raised on {sourceName}.")

End Sub

' Debug output when CustomButton is clicked:
' Routed event handler attached to CustomButton,
'     triggered by the ConditionalClick routed event raised on CustomButton.
' Routed event handler attached to StackPanel1,
'     triggered by the ConditionalClick routed event raised on CustomButton.

Quando CustomButton é clicado:

  1. O ConditionalClick evento roteado é gerado em CustomButton.
  2. O Handler_ConditionalClick manipulador de eventos anexado ao CustomButton é acionado.
  3. O ConditionalClick evento roteado percorre a árvore de elementos até StackPanel1.
  4. O Handler_ConditionalClick manipulador de eventos anexado ao StackPanel1 é acionado.
  5. O ConditionalClick evento roteado continua até a árvore de elementos potencialmente acionando outros manipuladores de eventos anexados a outros ConditionalClick elementos atravessados.

O Handler_ConditionalClick manipulador de eventos obtém as seguintes informações sobre o evento que o disparou:

  • O objeto sender , que é o elemento ao qual o manipulador de eventos está anexado. CustomButton Será sender a primeira vez que o manipulador será executado e StackPanel1 a segunda vez.
  • O RoutedEventArgs.Source objeto, que é o elemento que originalmente gerou o evento. Neste exemplo, o Source é sempre CustomButton.

Observação

Uma diferença fundamental entre um evento roteado e um evento CLR é que um evento roteado atravessa a árvore de elementos, procurando manipuladores, enquanto um evento CLR não atravessa a árvore de elementos e os manipuladores só podem se conectar ao objeto de origem que gerou o evento. Como resultado, um evento sender roteado pode ser qualquer elemento atravessado na árvore de elementos.

Você pode criar um evento de encapsulamento da mesma maneira que um evento borbulhante, exceto que você definirá a estratégia de roteamento na chamada de registro de evento como Tunnel. Para obter mais informações sobre eventos de encapsulamento, consulte Eventos de entrada do WPF.

Confira também