Compartir a través de


Vista previa de eventos

Los eventos de vista previa, también conocidos como eventos de tunelización, son eventos enrutados que atraviesan hacia abajo el árbol de elementos desde el elemento raíz de la aplicación hasta el elemento que generó el evento. El elemento que genera un evento se notifica como Source en los datos del evento. No todos los escenarios de eventos admiten o requieren eventos en versión preliminar. En este artículo se describe dónde existen los eventos de vista previa y cómo las aplicaciones o componentes pueden interactuar con ellos. Para obtener información sobre cómo crear un evento en versión preliminar, vea Cómo crear un evento enrutado personalizado.

Prerrequisitos

El artículo supone un conocimiento básico de los eventos enrutados y que hayas leído Información general sobre eventos enrutados. Para seguir los ejemplos de este artículo, le ayuda si está familiarizado con el lenguaje de marcado extensible de aplicaciones (XAML) y sabe cómo escribir aplicaciones de Windows Presentation Foundation (WPF).

Vista previa de eventos marcados como controlados

Tenga cuidado al marcar los eventos de vista previa como gestionados en los datos del evento. Marcar un evento de vista previa como controlado en un elemento distinto del elemento que lo generó puede impedir que el elemento que lo generó controle el evento. A veces, marcar los eventos de vista previa como controlados es intencional. Por ejemplo, un control compuesto podría suprimir eventos generados por componentes individuales y reemplazarlos por eventos generados por el control completo. Los eventos personalizados de un control pueden proporcionar datos de eventos personalizados y desencadenarse en función de las relaciones de estado del componente.

En el caso de los eventos de entrada, los datos del evento se comparten entre la versión preliminar y los equivalentes no de versión preliminar (burbujeo) de cada uno de estos eventos. Si utiliza un controlador de clase de evento en versión preliminar para marcar un evento de entrada como manejado, los controladores de clase para el evento de entrada de burbujeo normalmente no serán invocados. Si usa un manejador de instancia de evento de vista previa para marcar un evento como controlado, los manejadores de instancia para el evento de entrada burbujeante normalmente no se invocarán. Aunque puede configurar controladores de clase e instancia para que se invoquen incluso si un evento está marcado como controlado, esa configuración del controlador no es común. Para obtener más información sobre el control de clases y cómo se relaciona con los eventos en versión preliminar, vea Marcar eventos enrutados como controlados y control de clases.

Nota:

No todos los eventos de vista previa son eventos de tunelización . Por ejemplo, el PreviewMouseLeftButtonDown evento de entrada sigue una ruta hacia abajo a través del árbol de elementos, pero es un evento enrutado directo que se genera y vuelve a generar por cada UIElement en la ruta.

Manejo de la supresión de eventos por controles

Algunos controles compuestos suprimen los eventos de entrada en el nivel de componente para reemplazarlos por un evento personalizado de alto nivel. Por ejemplo, WPF ButtonBase marca el MouseLeftButtonDown evento de entrada de burbujeo como manejado en su OnMouseLeftButtonDown método y genera el Click evento. El MouseLeftButtonDown evento y sus datos de evento siguen continuando a lo largo de la ruta del árbol de elementos, pero dado que el evento está marcado como Handled en los datos de eventos, solo se invocan los controladores configurados para responder a eventos controlados.

Si desea que otros elementos más cerca de la raíz de su aplicación gestionen un evento enrutado marcado como gestionado, puede:

  • Asocie controladores llamando al UIElement.AddHandler(RoutedEvent, Delegate, Boolean) método y estableciendo el parámetro handledEventsToo en true. Este enfoque requiere adjuntar el controlador de eventos en código subyacente, después de obtener una referencia de objeto al elemento al que se asociará.

  • Si el evento marcado como controlado es un evento de burbujeo, adjunte controladores para el evento de vista previa equivalente si se encuentra disponible. Por ejemplo, si un control suprime el MouseLeftButtonDown evento, puede adjuntar un controlador para el PreviewMouseLeftButtonDown evento en su lugar. Este enfoque solo funciona para eventos de entrada de elementos base que implementan estrategias de enrutamiento de encapsulación y burbujeo y comparten datos de eventos.

En el ejemplo siguiente se implementa un control personalizado rudimentario denominado componentWrapper que contiene un TextBox. El control se agrega a un StackPanel objeto denominado 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>

El componentWrapper control escucha el KeyDown evento burbujeante que genera su TextBox componente cada vez que se produce una pulsación de tecla. En esa ocasión, el componentWrapper control:

  1. Marca el evento enrutado de burbujeo KeyDown como manejado para suprimirlo. Como resultado, solo se desencadena el outerStackPanel controlador configurado en código subyacente para responder a eventos controladosKeyDown . No se invoca el controlador outerStackPanel asociado para KeyDown eventos en XAML.

  2. Genera un evento enrutado de propagación personalizado denominado CustomKey, que activa el manejador outerStackPanel para el evento CustomKey.

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

En el ejemplo se muestran dos alternativas para que el evento enrutado suprimido KeyDown invoque un controlador de eventos adjunto a outerStackPanel.

  • Adjunte un PreviewKeyDown controlador de eventos al outerStackPanel. Dado que un evento enrutado de entrada de vista previa precede al evento enrutado de propagación equivalente, el PreviewKeyDown controlador del ejemplo se ejecuta antes del KeyDown controlador que suprime los eventos de vista previa y propagación a través de sus datos de eventos compartidos.

  • Adjunta un KeyDown controlador de eventos al outerStackPanel utilizando el método UIElement.AddHandler(RoutedEvent, Delegate, Boolean) en el código detrás, con el parámetro handledEventsToo establecido en true.

Nota:

Marcar los eventos de entrada, ya sea en versión preliminar o definitiva, como gestionados es una estrategia para suprimir los eventos generados por los componentes de un control. El enfoque que use depende de los requisitos de la aplicación.

Consulte también