Visualizar eventos (WPF .NET)

Os eventos de visualização, também conhecidos como eventos de encapsulamento, são eventos roteados que atravessam a árvore de elementos do elemento raiz do aplicativo para o elemento que gerou o evento. O elemento que gera um evento é relatado como os dados do Source evento. Nem todos os cenários de eventos oferecem suporte ou exigem eventos de visualização. Este artigo descreve onde existem eventos de visualização e como aplicativos ou componentes podem interagir com eles. Para obter informações sobre como criar um evento de visualização, consulte Como criar 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).

Visualizar eventos marcados como manipulados

Seja cauteloso ao marcar eventos de visualização como manipulados nos dados do evento. Marcar um evento de visualização como manipulado em um elemento diferente do elemento que o gerou pode impedir que o elemento que o gerou manipule o evento. Às vezes, marcar eventos de visualização como manipulados é intencional. Por exemplo, um controle composto pode suprimir eventos gerados por componentes individuais e substituí-los por eventos gerados pelo controle completo. Eventos personalizados para um controle podem fornecer dados de evento personalizados e disparar com base em relações de estado de componente.

Para eventos de entrada, os dados do evento são compartilhados pelos equivalentes de visualização e não visualização (borbulhamento) de cada evento. Se você usar um manipulador de classe de evento de visualização para marcar um evento de entrada como manipulado, os manipuladores de classe para o evento de entrada borbulhante normalmente não serão chamados. Ou, se você usar um manipulador de instância de evento de visualização para marcar um evento como manipulado, os manipuladores de instância para o evento de entrada borbulhante normalmente não serão chamados. Embora você possa configurar manipuladores de classe e instância para serem invocados mesmo se um evento for marcado como manipulado, essa configuração de manipulador não é comum. Para obter mais informações sobre a manipulação de classes e como elas se relacionam com eventos de visualização, consulte Marcando eventos roteados como manipulados e manipulação de classe.

Observação

Nem todos os eventos de visualização são eventos de encapsulamento . Por exemplo, o PreviewMouseLeftButtonDown evento de entrada segue uma rota descendente através da árvore de elementos, mas é um evento roteado direto que é gerado e regerado por cada UIElement um na rota.

Solucionando a supressão de eventos por controles

Alguns controles compostos suprimem eventos de entrada no nível do componente para substituí-los por um evento de alto nível personalizado. Por exemplo, o WPF ButtonBase marca o evento de entrada borbulhante como manipulado em seu OnMouseLeftButtonDown método e gera o MouseLeftButtonDownClick evento. O MouseLeftButtonDown evento e seus dados de evento ainda continuam ao longo da rota da árvore de elementos, mas como o evento é marcado como Handled em dados de evento, somente os manipuladores configurados para responder a eventos manipulados são chamados.

Se você quiser que outros elementos em direção à raiz do seu aplicativo manipulem um evento roteado marcado como manipulado, você pode:

O exemplo a seguir implementa um controle personalizado rudimentar chamado componentWrapper que contém um TextBoxarquivo . O controle é adicionado a um StackPanel arquivo outerStackPanel.

<StackPanel Name="outerStackPanel"
    VerticalAlignment="Center"
    custom:ComponentWrapper.CustomKey="Handler_PrintEventInfo"
    TextBox.KeyDown="Handler_PrintEventInfo"
    TextBox.PreviewKeyDown="Handler_PrintEventInfo" >
    <custom:ComponentWrapper
        x:Name="componentWrapper"
        TextBox.KeyDown="ComponentWrapper_KeyDown"
        custom:ComponentWrapper.CustomKey="Handler_PrintEventInfo"
        HorizontalAlignment="Center">
        <TextBox Name="componentTextBox" Width="200" KeyDown="Handler_PrintEventInfo" />
    </custom:ComponentWrapper>
</StackPanel>

O componentWrapper controle escuta o KeyDown evento borbulhante gerado por seu TextBox componente sempre que ocorre um pressionamento de tecla. Nessa ocorrência, o componentWrapper controle:

  1. Marca o evento roteado borbulhante como manipulado KeyDown para suprimi-lo. Como resultado, somente o outerStackPanel manipulador configurado no code-behind para responder a eventos manipuladosKeyDown é acionado. O outerStackPanel manipulador anexado em XAML para KeyDown eventos não é invocado.

  2. Gera um evento roteado borbulhante personalizado chamado CustomKey, que dispara o manipulador para o outerStackPanelCustomKey evento.

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();

        // Attach a handler on outerStackPanel that will be invoked by handled KeyDown events.
        outerStackPanel.AddHandler(KeyDownEvent, new RoutedEventHandler(Handler_PrintEventInfo), 
            handledEventsToo: true);
    }

    private void ComponentWrapper_KeyDown(object sender, System.Windows.Input.KeyEventArgs e)
    {
        Handler_PrintEventInfo(sender, e);

        Debug.WriteLine("KeyDown event marked as handled on componentWrapper.\r\n" +
            "CustomKey event raised on componentWrapper.");

        // Mark the event as handled.
        e.Handled = true;

        // Raise the custom click event.
        componentWrapper.RaiseCustomRoutedEvent();
    }

    private void Handler_PrintEventInfo(object sender, System.Windows.Input.KeyEventArgs e)
    {
        string senderName = ((FrameworkElement)sender).Name;
        string sourceName = ((FrameworkElement)e.Source).Name;
        string eventName = e.RoutedEvent.Name;
        string handledEventsToo = e.Handled ? " Parameter handledEventsToo set to true." : "";

        Debug.WriteLine($"Handler attached to {senderName} " +
            $"triggered by {eventName} event raised on {sourceName}.{handledEventsToo}");
    }

    private void Handler_PrintEventInfo(object sender, RoutedEventArgs e)
    {
        string senderName = ((FrameworkElement)sender).Name;
        string sourceName = ((FrameworkElement)e.Source).Name;
        string eventName = e.RoutedEvent.Name;
        string handledEventsToo = e.Handled ? " Parameter handledEventsToo set to true." : "";

        Debug.WriteLine($"Handler attached to {senderName} " +
            $"triggered by {eventName} event raised on {sourceName}.{handledEventsToo}");
    }

    // Debug output:
    //
    // Handler attached to outerStackPanel triggered by PreviewKeyDown event raised on componentTextBox.
    // Handler attached to componentTextBox triggered by KeyDown event raised on componentTextBox.
    // Handler attached to componentWrapper triggered by KeyDown event raised on componentTextBox.
    // KeyDown event marked as handled on componentWrapper.
    // CustomKey event raised on componentWrapper.
    // Handler attached to componentWrapper triggered by CustomKey event raised on componentWrapper.
    // Handler attached to outerStackPanel triggered by CustomKey event raised on componentWrapper.
    // Handler attached to outerStackPanel triggered by KeyDown event raised on componentTextBox. Parameter handledEventsToo set to true.
}

public class ComponentWrapper : StackPanel
{
    // Register a custom routed event using the Bubble routing strategy.
    public static readonly RoutedEvent CustomKeyEvent = 
        EventManager.RegisterRoutedEvent(
            name: "CustomKey",
            routingStrategy: RoutingStrategy.Bubble,
            handlerType: typeof(RoutedEventHandler),
            ownerType: typeof(ComponentWrapper));

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

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

        // Raise the event, which will bubble up through the element tree.
        RaiseEvent(routedEventArgs);
    }
}
Partial Public Class MainWindow
        Inherits Window

        Public Sub New()
        InitializeComponent()

        ' Attach a handler on outerStackPanel that will be invoked by handled KeyDown events.
        outerStackPanel.[AddHandler](KeyDownEvent, New RoutedEventHandler(AddressOf Handler_PrintEventInfo),
                                     handledEventsToo:=True)
    End Sub

    Private Sub ComponentWrapper_KeyDown(sender As Object, e As KeyEventArgs)
        Handler_PrintEventInfo(sender, e)
        Debug.WriteLine("KeyDown event marked as handled on componentWrapper." &
                        vbCrLf & "CustomKey event raised on componentWrapper.")

        ' Mark the event as handled.
        e.Handled = True

        ' Raise the custom click event.
        componentWrapper.RaiseCustomRoutedEvent()
    End Sub

    Private Sub Handler_PrintEventInfo(sender As Object, e As KeyEventArgs)
        Dim senderName As String = CType(sender, FrameworkElement).Name
        Dim sourceName As String = CType(e.Source, FrameworkElement).Name
        Dim eventName As String = e.RoutedEvent.Name
        Dim handledEventsToo As String = If(e.Handled, " Parameter handledEventsToo set to true.", "")
        Debug.WriteLine($"Handler attached to {senderName} " &
                        $"triggered by {eventName} event raised on {sourceName}.{handledEventsToo}")
    End Sub

    Private Sub Handler_PrintEventInfo(sender As Object, e As RoutedEventArgs)
        Dim senderName As String = CType(sender, FrameworkElement).Name
        Dim sourceName As String = CType(e.Source, FrameworkElement).Name
        Dim eventName As String = e.RoutedEvent.Name
        Dim handledEventsToo As String = If(e.Handled, " Parameter handledEventsToo set to true.", "")
        Debug.WriteLine($"Handler attached to {senderName} " &
                        $"triggered by {eventName} event raised on {sourceName}.{handledEventsToo}")
    End Sub

    ' Debug output
    '
    ' Handler attached to outerStackPanel triggered by PreviewKeyDown event raised on componentTextBox.
    ' Handler attached to componentTextBox triggered by KeyDown event raised on componentTextBox.
    ' Handler attached to componentWrapper triggered by KeyDown event raised on componentTextBox.
    ' KeyDown event marked as handled on componentWrapper.
    ' CustomKey event raised on componentWrapper.
    ' Handler attached to componentWrapper triggered by CustomKey event raised on componentWrapper.
    ' Handler attached to outerStackPanel triggered by CustomKey event raised on componentWrapper.
    ' Handler attached to outerStackPanel triggered by KeyDown event raised on componentTextBox. Parameter handledEventsToo set to true.
End Class

    Public Class ComponentWrapper
        Inherits StackPanel

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

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

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

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

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

        End Event

    Public Sub RaiseCustomRoutedEvent()
        ' Create a RoutedEventArgs instance & raise the event,
        ' which will bubble up through the element tree.
        Dim routedEventArgs As New RoutedEventArgs(routedEvent:=CustomKeyEvent)
            [RaiseEvent](routedEventArgs)
        End Sub
    End Class

O exemplo demonstra duas soluções alternativas para obter o evento roteado suprimido KeyDown para invocar um manipulador de eventos anexado outerStackPanelao :

  • Anexar um PreviewKeyDown manipulador de eventos ao outerStackPanel. Como um evento roteado de entrada de visualização precede o evento roteado borbulhante equivalente, o PreviewKeyDown manipulador no exemplo é executado à frente do KeyDown manipulador que suprime eventos de visualização e borbulhamento por meio de seus dados de evento compartilhados.

  • Anexe um KeyDown manipulador de outerStackPanel eventos ao usando o método em code-behind, com o UIElement.AddHandler(RoutedEvent, Delegate, Boolean)handledEventsToo parâmetro definido como true.

Observação

Marcar equivalentes de visualização ou não visualização de eventos de entrada conforme manipulados são estratégias para suprimir eventos gerados pelos componentes de um controle. A abordagem usada depende dos requisitos do aplicativo.

Confira também