Compartilhar via


Como criar um evento roteado personalizado (WPF .NET)

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

Pré-requisitos

O artigo pressupõe um conhecimento básico dos eventos roteados e que você leu Visão geral dos eventos roteados. Para seguir os exemplos neste artigo, ele ajuda se você estiver familiarizado com XAML (Extensible Application Markup Language) e saber como escrever aplicativos do WPF (Windows Presentation Foundation).

Etapas do evento roteado

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

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

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

    • O identificador de um evento roteado com uma estratégia de propagação é denominado <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 túnel é denominado Preview<event name>Event. Por exemplo, se o nome do evento for Tap, o identificador deverá ser nomeado PreviewTapEvent.
  3. Defina os acessadores de evento add e remove do CLR. Sem acessadores de eventos CLR, você só poderá adicionar ou remover manipuladores de eventos por meio de chamadas diretas aos métodos UIElement.AddHandler e UIElement.RemoveHandler. Com os acessadores de eventos CLR, você obtém estes mecanismos de atribuição de 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 acionar 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 classe CustomButton em uma biblioteca de controle personalizada. A classe CustomButton, que deriva de Button:

  1. Registra um RoutedEvent chamado ConditionalClick usando o método RegisterRoutedEvent e especifica a estratégia de propagação durante o registro.
  2. Atribui a instância RoutedEvent, retornada da chamada de registro, a um campo estático de somente leitura chamado ConditionalClickEvent.
  3. Define os acessadores de evento add e remove do CLR.
  4. Adiciona lógica personalizada para gerar o evento roteado personalizado quando o CustomButton é clicado e uma condição externa se aplica. Embora o código de exemplo gere o evento roteado ConditionalClick de dentro do método virtual OnClick sobrescrito, você pode gerar o 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 um StackPanele atribuir o método Handler_ConditionalClick como o manipulador de eventos ConditionalClick para os elementos CustomButton 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>

Em code-behind, o aplicativo WPF define o método de manipulador de eventos Handler_ConditionalClick. Os métodos do 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 evento roteado ConditionalClick é gerado em CustomButton.
  2. O manipulador de eventos Handler_ConditionalClick anexado ao CustomButton é acionado.
  3. O evento roteado ConditionalClick percorre a árvore de elementos até StackPanel1.
  4. O manipulador de eventos Handler_ConditionalClick associado ao StackPanel1 é disparado.
  5. O evento roteado ConditionalClick continua subindo pela árvore de elementos, disparando potencialmente outros manipuladores de eventos ConditionalClick anexados a outros elementos percorridos.

O manipulador de eventos Handler_ConditionalClick 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. O sender será CustomButton na primeira vez que o manipulador for executado e StackPanel1 na segunda vez.
  • O objeto RoutedEventArgs.Source, que é o elemento que originalmente gerou o evento. Neste exemplo, o Source é sempre CustomButton.

Nota

Uma diferença importante 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 anexar ao objeto de origem que gerou o evento. Como resultado, um evento roteado sender pode ser qualquer elemento percorrido na árvore de elementos.

Você pode criar um evento de túnel da mesma maneira que um evento de propagação, exceto que você define a estratégia de roteamento na chamada de registro de evento para Tunnel. Para mais informações sobre eventos de túnel, consulte Eventos de entrada do WPF.

Consulte também