Información general sobre eventos enrutados (WPF .NET)

Los desarrolladores de aplicaciones y los autores de componentes de Windows Presentation Foundation (WPF) pueden usar eventos enrutados para propagar eventos a través de un árbol de elementos e invocar controladores de eventos en varios clientes de escucha del árbol. Estas características no se encuentran en eventos de Common Language Runtime (CLR). Varios eventos de WPF son eventos enrutados, como ButtonBase.Click. En este artículo se tratan los conceptos de eventos enrutados básicos y se ofrecen instrucciones sobre cuándo y cómo se debe responder a eventos enrutados.

Importante

La documentación de la guía de escritorio para .NET 7 y .NET 6 está en proceso de elaboración.

Prerrequisitos

En este artículo se da por supuesto un conocimiento básico de Common Language Runtime (CLR), la programación orientada a objetos y cómo el diseño de elementos WPF se puede conceptualizar como árbol. Para seguir los ejemplos de este artículo, resultará útil que esté familiarizado con lenguaje XAML y sepa cómo escribir aplicaciones WPF.

¿Qué es un evento enrutado?

Puede considerar los eventos enrutados desde una perspectiva funcional o de implementación:

  • Desde una perspectiva funcional, un evento enrutado es un tipo de evento que puede invocar controladores en varios clientes de escucha de un árbol de elementos, no solo en el origen del evento. Un cliente de escucha de eventos es el elemento en que se adjunta e invoca un controlador de eventos. Un origen de eventos es el elemento u objeto que generó originalmente un evento.

  • Desde una perspectiva de implementación, un evento enrutado es un evento registrado con el sistema de eventos de WPF, respaldado por una instancia de la clase RoutedEvent y procesado por el mencionado sistema. Normalmente, un evento enrutado se implementa con un "contenedor" de eventos CLR para habilitar la asociación de controladores en XAML y en el código subyacente como lo haría con un evento CLR.

Las aplicaciones WPF suelen contener muchos elementos, que se declararon en XAML o crearon una instancia en el código. Los elementos de una aplicación existen dentro de su árbol de elementos. Según la definición de un evento enrutado, cuando el evento se genera en un elemento de origen, este:

  • Se propaga a través del árbol de elementos desde el elemento de origen al elemento raíz, que suele ser una página o ventana.
  • Se tuneliza a través del árbol de elementos desde el elemento raíz al elemento de origen.
  • No viaja a través del árbol de elementos y solo se produce en el elemento de origen.

Tenga en cuenta el siguiente árbol de elementos parcial:

<Border Height="30" Width="200" BorderBrush="Gray" BorderThickness="1">
    <StackPanel Background="LightBlue" Orientation="Horizontal" Button.Click="YesNoCancelButton_Click">
        <Button Name="YesButton">Yes</Button>
        <Button Name="NoButton">No</Button>
        <Button Name="CancelButton">Cancel</Button>
    </StackPanel>
</Border>

El árbol de elementos se representa como se muestra:

Botones Sí, No y Cancelar.

Cada uno de los tres botones es un posible origen del evento Click. Cuando se hace clic en uno de los botones, genera el evento Click que se propaga desde el botón al elemento raíz. Los elementos Button y Border no tienen controladores de eventos adjuntos, pero StackPanel sí. Es posible que otros elementos situados más arriba en el árbol que no se muestran también tengan controladores de eventos Click adjuntos. Cuando el evento Click alcanza el elemento StackPanel, el sistema de eventos de WPF invoca el controlador YesNoCancelButton_Click adjunto. La ruta de eventos del evento Click en el ejemplo es: Button ->StackPanel ->Border -> elementos primarios sucesivos.

Nota

El elemento que generó originalmente un evento enrutado se identifica como RoutedEventArgs.Source en los parámetros del controlador de eventos. El cliente de escucha de eventos es el elemento en que se adjunta e invoca el controlador de eventos, y se identifica como emisor en los parámetros del controlador de eventos.

Escenarios de nivel superior para los eventos enrutados

Estos son algunos de los escenarios que han motivado el concepto de evento enrutado y lo distinguen de un evento CLR típico:

  • Encapsulación y composición de controles: varios controles de WPF tienen un modelo de contenido enriquecido. Por ejemplo, se puede colocar una imagen dentro de un control Button, lo que de hecho extiende el árbol visual del botón. Sin embargo, la imagen agregada no debe interrumpir el comportamiento de la prueba de posicionamiento del botón, que debe responder cuando un usuario hace clic en los píxeles de la imagen.

  • Puntos de unión de controladores únicos: puede registrar un controlador para el evento Click de cada botón, pero con los eventos enrutados puede adjuntar un solo controlador como se muestra en el ejemplo de XAML anterior. Esto le permite realizar cambios en el árbol de elementos del controlador singular, como, por ejemplo, agregar o quitar más botones, sin tener que registrar el evento Click de cada botón. Cuando se genera el evento Click, la lógica del controlador puede determinar el origen del evento. El siguiente controlador, especificado en el árbol de elementos XAML mostrado anteriormente, contiene esa lógica:

    private void YesNoCancelButton_Click(object sender, RoutedEventArgs e)
    {
        FrameworkElement sourceFrameworkElement = e.Source as FrameworkElement;
        switch (sourceFrameworkElement.Name)
        {
            case "YesButton":
                // YesButton logic.
                break;
            case "NoButton":
                // NoButton logic.
                break;
            case "CancelButton":
                // CancelButton logic.
                break;
        }
        e.Handled = true;
    }
    
    Private Sub YesNoCancelButton_Click(sender As Object, e As RoutedEventArgs)
        Dim frameworkElementSource As FrameworkElement = TryCast(e.Source, FrameworkElement)
    
        Select Case frameworkElementSource.Name
            Case "YesButton"
                ' YesButton logic.
            Case "NoButton"
                ' NoButton logic.
            Case "CancelButton"
                ' CancelButton logic.
        End Select
    
        e.Handled = True
    End Sub
    
  • Control de clases: los eventos enrutados admiten un controlador de eventos de clase que se define en una clase. Los controladores de clase controlan un evento antes que cualquier controlador de instancia para el mismo evento en cualquier instancia de la clase.

  • Hacer referencia a un evento sin reflexión: cada evento enrutado crea un identificador de campo RoutedEvent para proporcionar una sólida técnica de identificación de eventos que no requiere la reflexión estática o en tiempo de ejecución para identificar el evento.

Cómo se implementan los eventos enrutados

Un evento enrutado es un evento registrado con el sistema de eventos de WPF, respaldado por una instancia de la clase RoutedEvent y procesado por el mencionado sistema. La instancia de RoutedEvent, obtenida del registro, se almacena normalmente como miembro public static readonly de la clase que se ocupó de su registro. Esa clase se conoce como la clase "de propietario" del evento. Normalmente, un evento enrutado implementa un "contenedor" de eventos CLR con nombre idéntico. El contenedor de eventos CLR incluye los descriptores de acceso add y remove para habilitar la asociación de controladores en XAML y en el código subyacente a través de la sintaxis de eventos específica del lenguaje. Los descriptores de acceso add y remove invalidan su implementación de CLR y llaman a los métodos AddHandler y RemoveHandler de evento enrutado. El mecanismo de conexión y de respaldo del evento enrutado es conceptualmente similar al modo en que una propiedad de dependencia es una propiedad de CLR respaldada por la clase DependencyProperty y registrada en el sistema de propiedades de WPF.

En el ejemplo siguiente se registra el evento enrutado Tap, se almacena la instancia RoutedEvent devuelta y se implementa un contenedor de eventos CLR.

// Register a custom routed event using the Bubble routing strategy.
public static readonly RoutedEvent TapEvent = EventManager.RegisterRoutedEvent(
    name: "Tap",
    routingStrategy: RoutingStrategy.Bubble,
    handlerType: typeof(RoutedEventHandler),
    ownerType: typeof(CustomButton));

// Provide CLR accessors for adding and removing an event handler.
public event RoutedEventHandler Tap
{
    add { AddHandler(TapEvent, value); }
    remove { RemoveHandler(TapEvent, value); }
}
' Register a custom routed event using the Bubble routing strategy.
Public Shared ReadOnly TapEvent As RoutedEvent = EventManager.RegisterRoutedEvent(
    name:="Tap",
    routingStrategy:=RoutingStrategy.Bubble,
    handlerType:=GetType(RoutedEventHandler),
    ownerType:=GetType(CustomButton))

' Provide CLR accessors for adding and removing an event handler.
Public Custom Event Tap As RoutedEventHandler
    AddHandler(value As RoutedEventHandler)
        [AddHandler](TapEvent, value)
    End AddHandler

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

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

Estrategias de enrutamiento

Los eventos enrutados usan una de estas tres estrategias de enrutamiento:

  • Propagación: inicialmente, se invocan los controladores de eventos en el origen del evento. Después, el evento enrutado va pasando por los elementos primarios sucesivos mediante la invocación a su vez de sus controladores de eventos hasta alcanzar la raíz del árbol de elementos. La mayoría de los eventos enrutados usan la estrategia del enrutamiento de propagación. Los eventos con enrutamiento de propagación generalmente se usan para informar sobre cambios de entrada o de estado procedentes de controles compuestos u otros elementos de la interfaz de usuario.

  • Tunelización: inicialmente, se invocan los controladores de eventos en la raíz del árbol de elementos. Después, el evento enrutado va pasando por los elementos secundarios sucesivos mediante la invocación a su vez de sus controladores de eventos hasta alcanzar el origen del evento. Los eventos que siguen una ruta de tunelización también se conocen como eventos de vista previa. Los eventos de entrada de WPF, por regla general, se implementan como pares de vista previa y propagación.

  • Directa: solo se invocan los controladores de eventos en el origen del evento. Esta estrategia de no enrutamiento es análoga a los eventos del marco de interfaz de usuario de Windows Forms, que son eventos CLR estándar. A diferencia de los eventos CLR, los eventos enrutados directos admiten el control de clases y EventSetters y EventTriggers pueden usarlos.

¿Por qué usar eventos enrutados?

Como desarrollador de aplicaciones, no siempre necesita saber ni preocuparse de si el evento que está controlando se implementa como un evento enrutado. Los eventos enrutados tienen un comportamiento especial, pero ese comportamiento es prácticamente invisible si está controlando un evento en el elemento que lo generó. Sin embargo, los eventos enrutados son pertinentes si desea adjuntar un controlador de eventos a un elemento primario para controlar los eventos generados por elementos secundarios, como, por ejemplo, dentro de un control compuesto.

Los clientes de escucha de los eventos enrutados no necesitan los eventos enrutados que controlan para ser miembros de su clase. Cualquier UIElement o ContentElement puede ser un cliente de escucha de evento para cualquier evento enrutado. Dado que los elementos visuales derivan de UIElement o ContentElement, puede usar eventos enrutados como una "interfaz" conceptual que admite el intercambio de información de eventos entre distintos elementos de una aplicación. El concepto de "interfaz" para los eventos enrutados es especialmente aplicable a los eventos de entrada.

Los eventos enrutados admiten el intercambio de información de eventos entre elementos en la ruta de eventos porque cada cliente de escucha tiene acceso a la misma instancia de datos de evento. Si un elemento cambia algo en los datos de evento, ese cambio es visible para los elementos posteriores en la ruta de eventos.

Aparte del aspecto del enrutamiento, puede optar por implementar un evento enrutado en lugar de un evento CLR estándar por estas razones:

  • Algunas características de plantillas y estilos de WPF, como EventSetters y EventTriggers, requieren que el evento al que se hace referencia sea un evento enrutado.

  • Los eventos enrutados admiten controladores de eventos de clase que controlan un evento antes que cualquier controlador de instancia para el mismo evento en cualquier instancia de la clase de cliente de escucha. Esta característica es útil en el diseño de controles porque el controlador de clase puede exigir comportamientos de clase orientados a eventos que un controlador de instancia no puede suprimir.

Asociación e implementación de un controlador de eventos enrutados

En XAML, se adjunta un controlador de eventos a un elemento mediante la declaración del nombre del evento como un atributo en el elemento de cliente de escucha de eventos. El valor del atributo es el nombre del método de control. El método de control debe implementarse en la clase parcial de código subyacente para la página de XAML. El cliente de escucha de eventos es el elemento en que se adjunta e invoca el controlador de eventos.

Para un evento que es miembro (heredado o no) de la clase de cliente de escucha, puede adjuntar un controlador de la siguiente manera:

<Button Name="Button1" Click="Button_Click">Click me</Button>

Si el evento no es miembro de la clase del cliente de escucha, debe usar el nombre completo de evento en el formato <owner type>.<event name>. Por ejemplo, dado que la clase StackPanel no implementa el evento Click, para adjuntar un controlador a un elemento StackPanel para un evento Click que aparece en ese elemento, deberá usar la sintaxis de nombre completo de evento:

<StackPanel Name="StackPanel1" Button.Click="Button_Click">
    <Button>Click me</Button>
</StackPanel>

La firma del método de control de eventos en el código subyacente debe coincidir con el tipo de delegado del evento enrutado. El parámetro sender del delegado RoutedEventHandler del evento Click especifica el elemento al que se adjunta el controlador de eventos. El parámetro args del delegado RoutedEventHandler contiene los datos de evento. Una implementación de código subyacente compatible para el controlador de eventos Button_Click podría ser:

private void Button_Click(object sender, RoutedEventArgs e)
{
    // Click event logic.
}
Private Sub Button_Click(sender As Object, e As RoutedEventArgs)
    ' Click event logic.
End Sub

Aunque RoutedEventHandler es el delegado del controlador de eventos enrutado básico, algunos controles o escenarios de implementación requieren delegados diferentes que admitan datos de evento más especializados. Por ejemplo, para el evento enrutado DragEnter, el controlador debe implementar el delegado DragEventHandler. De esta forma, el código de controlador puede acceder a la propiedad DragEventArgs.Data en los datos de evento, que contienen la carga del portapapeles de la operación de arrastrar.

La sintaxis XAML para agregar controladores de eventos enrutados es la misma que para los controladores de eventos CLR estándar. Para más información sobre cómo agregar controladores de eventos en XAML, vea Información general sobre XAML (WPF). Para obtener un ejemplo completo de cómo adjuntar un controlador de eventos a un elemento mediante XAML, consulte Cómo: Controlar un evento enrutado.

A fin de adjuntar un controlador de eventos de un evento enrutado a un elemento mediante código, generalmente se tienen dos opciones:

  • Llame directamente al método AddHandler. Los controladores de eventos enrutados siempre se pueden adjuntar de esta manera. En este ejemplo se adjunta un controlador de eventos Click a un botón mediante el método AddHandler:

    Button1.AddHandler(ButtonBase.ClickEvent, new RoutedEventHandler(Button_Click));
    
    Button1.[AddHandler](ButtonBase.ClickEvent, New RoutedEventHandler(AddressOf Button_Click))
    

    A fin de adjuntar un controlador para el evento Click del botón a otro elemento de la ruta del evento, como un elemento StackPanel denominado StackPanel1:

    StackPanel1.AddHandler(ButtonBase.ClickEvent, new RoutedEventHandler(Button_Click));
    
    StackPanel1.[AddHandler](ButtonBase.ClickEvent, New RoutedEventHandler(AddressOf Button_Click))
    
  • Si el evento enrutado implementa un contenedor de eventos CLR, use la sintaxis de eventos específica del lenguaje para agregar controladores de eventos tal y como haría para un evento CLR estándar. La mayoría de los eventos enrutados de WPF existentes implementan el contenedor CLR, lo que permite la sintaxis de eventos específica del lenguaje. En este ejemplo se adjunta un controlador de eventos Click a un botón mediante la sintaxis específica del lenguaje:

    Button1.Click += Button_Click;
    
    AddHandler Button1.Click, AddressOf Button_Click
    

Para obtener un ejemplo de cómo adjuntar un controlador de eventos en el código, consulte Cómo: Agregar un controlador de eventos mediante código. Si va a codificar en Visual Basic, también puede usar la palabra clave Handles para agregar controladores como parte de las declaraciones de controlador. Para más información, vea Visual Basic y control de eventos de WPF (WPF .NET).

El concepto de controlado

Todos los eventos enrutados comparten una clase base común de los datos de evento, que es la clase RoutedEventArgs. La clase RoutedEventArgs define la propiedad Handled booleana. El propósito de la propiedad Handled es permitir que cualquier controlador de eventos en la ruta de eventos marque el evento enrutado como controlado. Para marcar un evento como controlado, establezca el valor de Handled en true en el código de controlador de eventos.

El valor de Handled afecta a cómo se procesa un evento enrutado a medida que recorre la ruta de eventos. Si Handled es true en los datos de evento compartidos de un evento enrutado, los controladores adjuntos a otros elementos más adelante en la ruta de eventos normalmente no se invocarán para esa instancia concreta del evento. Para la mayoría de los escenarios de controladores comunes, al marcar un evento como controlado se impide eficazmente que los controladores posteriores en la ruta de eventos, ya sean controladores de instancia o de clase, respondan a esa instancia concreta del evento. Sin embargo, en casos poco frecuentes en los que necesita que el controlador de eventos responda a eventos enrutados marcados como controlados, puede hacer lo siguiente:

El concepto de Handled podría afectar a la manera de diseñar la aplicación y codificar los controladores de eventos. Puede conceptualizar Handled como un protocolo simple para el procesamiento de eventos enrutados. La forma de usar este protocolo depende de usted, pero el uso esperado del parámetro Handled es el siguiente:

  • Si un evento enrutado está marcado como controlado, no es necesario que los demás elementos a lo largo de la ruta lo controlen de nuevo.

  • Si un evento enrutado no está marcado como controlado, los clientes de escucha anteriores en la ruta de eventos no tienen un controlador para el evento o ninguno de los controladores registrados respondió al evento de una manera que justifique marcar el evento como controlado. Los controladores del cliente de escucha actual tienen tres posibles líneas de acción:

    • No realice ninguna acción. El evento sigue estando sin controlar y se enruta al cliente de escucha siguiente en el árbol.

    • Ejecute código en respuesta al evento, pero no hasta un punto que justifique marcar el evento como controlado. El evento sigue estando sin controlar y se enruta al cliente de escucha siguiente en el árbol.

    • Ejecute código en respuesta al evento, hasta un punto que justifique marcar el evento como controlado. Marque el evento como controlado en los datos de evento. El evento sigue enrutándose al cliente de escucha siguiente en el árbol, pero la mayoría de los clientes de escucha no invocarán más controladores. La excepción son los clientes de escucha con controladores que se registraron específicamente con el mecanismo handledEventsToo establecido en true.

Para obtener más información sobre el control de eventos enrutados, consulte Marcar eventos enrutados como controlados y control de clases.

Aunque los desarrolladores que solo controlan un evento enrutado de propagación en el objeto que lo generó podrían no preocuparse por otros clientes de escucha, es recomendable marcar el evento como controlado de todos modos. Al hacerlo, se evitan efectos secundarios imprevistos si un elemento más adelante en la ruta de eventos tiene un controlador para el mismo evento enrutado.

Controladores de clase

Los controladores de eventos enrutados pueden ser controladores de instancia o controladores de clase. Los controladores de clase para una clase determinada se invocan antes de que cualquier controlador de instancia responda al mismo evento en cualquier instancia de esa clase. Debido a este comportamiento, cuando los eventos enrutados se marcan como controlados, a menudo se marcan como tales en los controladores de clase. Hay dos tipos de controladores de clase:

Algunos controles de WPF tienen el control de clase inherente para ciertos eventos enrutados. El control de clases podría dar la impresión de que el evento enrutado nunca se genera, pero en realidad está marcado como controlado por un controlador de clase. Si necesita que el controlador de eventos responda al evento controlado, puede registrar el controlador con el mecanismo handledEventsToo establecido en true. Para obtener más información sobre cómo implementar sus propios controladores de clase o cómo evitar el control de clase no deseado, consulte Marcar eventos enrutados como controlados y control de clases.

Eventos adjuntos en WPF

El lenguaje XAML también define un tipo especial de evento denominado evento adjunto. Los eventos adjuntos se pueden usar para definir un nuevo evento enrutado en una clase que no sea de elementos y generar ese evento en cualquier elemento del árbol. Para ello, debe registrar el evento adjunto como evento enrutado y proporcionar código de respaldo específico que admita la funcionalidad del evento adjunto. Dado que los eventos adjuntos están registrados como eventos enrutados, al generarse en un elemento se propagan a través del árbol de elementos.

En la sintaxis XAML, un evento adjunto se especifica por su nombre de evento y tipo de propietario, en el formato <owner type>.<event name>. Dado que el nombre del evento está calificado con el nombre de su tipo de propietario, la sintaxis permite que el evento se adjunte a cualquier elemento del que se pueda crear una instancia. Esta sintaxis también es aplicable a los controladores para eventos enrutados normales adjuntos a un elemento arbitrario en la ruta de eventos. También puede adjuntar controladores para eventos adjuntos en el código subyacente mediante una llamada al método AddHandler en el objeto al que debe adjuntarse el controlador.

El sistema de entrada de WPF emplea mucho los eventos adjuntos. Sin embargo, casi todos esos eventos adjuntos aparecen como eventos enrutados no adjuntos equivalentes a través de elementos base. Rara vez usará o controlará los eventos adjuntos directamente. Por ejemplo, es más fácil controlar el evento Mouse.MouseDown adjunto subyacente en un elemento UIElement a través del evento enrutado UIElement.MouseDown equivalente que mediante la sintaxis de los eventos adjuntos en XAML o el código subyacente.

Para más información sobre los eventos adjuntos en WPF, consulte Información general sobre eventos adjuntos.

Nombres de evento completos en XAML

La sintaxis <owner type>.<event name> califica un nombre de evento con el nombre de su tipo de propietario. Esta sintaxis permite adjuntar un evento a cualquier elemento, no solo a los elementos que implementan el evento como miembro de su clase. La sintaxis es aplicable al adjuntar controladores en XAML para eventos adjuntos o eventos enrutados en elementos arbitrarios en la ruta de eventos. Tenga en cuenta el escenario en el que desea adjuntar un controlador a un elemento primario para controlar los eventos enrutados generados en elementos secundarios. Si el elemento primario no tiene el evento enrutado como miembro, deberá usar la sintaxis de nombre completo de evento. Por ejemplo:

<StackPanel Name="StackPanel1" Button.Click="Button_Click">
    <Button>Click me</Button>
</StackPanel>

En el ejemplo, el cliente de escucha del elemento primario al que se agrega el controlador de eventos es un elemento StackPanel. Sin embargo, el evento enrutado Click se implementa y genera en la clase ButtonBase y está disponible para la clase Button a través de la herencia. Aunque la clase Button "posee" el evento Click, el sistema de eventos enrutados permite que los controladores de cualquier evento enrutado se adjunten a cualquier cliente de escucha de instancias de UIElement o ContentElement que, de lo contrario, podría tener controladores para un evento CLR. El espacio de nombres xmlns predeterminado para estos nombres de atributo de evento calificados suele ser el espacio de nombres xmlns de WPF predeterminado, pero también se pueden especificar espacios de nombres con prefijos para los eventos enrutados personalizados. Para más información sobre xmlns, consulte Espacios de nombres y asignación de espacios de nombres XAML para WPF.

Eventos de entrada de WPF

En la plataforma WPF, con frecuencia se emplean los eventos enrutados como eventos de entrada. Por convención, los eventos enrutados de WPF que siguen una ruta de tunelización tienen un nombre con el prefijo "Preview". El prefijo Preview significa que el evento de vista previa se completa antes de iniciarse el evento de propagación emparejado. Los eventos de entrada suelen presentarse en parejas, donde uno es un evento de vista previa y el otro es un evento enrutado de propagación. Por ejemplo, PreviewKeyDown y KeyDown. Los pares de eventos comparten la misma instancia de datos de eventos, que para PreviewKeyDown y KeyDown es de tipo KeyEventArgs. En ocasiones, los eventos de entrada solo tienen una versión de propagación o quizás solo una versión enrutada directa. En la documentación de la API, los temas sobre eventos enrutados contienen referencias cruzadas a los pares de eventos enrutados y clarifican la estrategia de enrutamiento para cada evento enrutado.

Los eventos de entrada de WPF que se presentan en parejas se implementan de forma que una única acción del usuario desde un dispositivo de entrada, como presionar un botón del mouse, desencadenará los eventos enrutados de vista previa y propagación secuencialmente. En primer lugar, se genera el evento de vista previa, que completa su ruta. Al finalizar el evento de vista previa, se genera el evento de propagación, que completa su ruta. La llamada de método RaiseEvent en la clase de implementación que genera el evento de propagación reutiliza los datos de evento del evento de vista previa para el evento de propagación.

Un evento de entrada de vista previa marcado como controlado no invocará ningún controlador de eventos registrado normalmente durante el resto de la ruta de vista previa y no se generará el evento de propagación emparejado. Este comportamiento de control es útil para los diseñadores de controles compuestos que desean que se informe de los eventos de entrada basados en pruebas de posicionamiento o de los eventos de entrada basados en el foco en el nivel superior de su control. Los elementos de nivel superior del control tienen la oportunidad de controlar desde la clase eventos de vista previa de subcomponentes de control para "reemplazarlos" por un evento específico del control de nivel superior.

Para ilustrar cómo funciona el procesamiento de eventos de entrada, observe el ejemplo de evento de entrada siguiente. En la ilustración de árbol siguiente, leaf element #2 es el origen de los eventos emparejados PreviewMouseDown y MouseDown:

Diagrama de enrutamiento de eventos.

El orden de procesamiento de los eventos después de una acción de presión del mouse en el elemento de hoja n.º 2 es:

  1. Evento de tunelización PreviewMouseDown en el elemento raíz.
  2. Evento de tunelización PreviewMouseDown en el elemento intermedio n.º 1.
  3. Evento de tunelización PreviewMouseDown en el elemento de hoja n.º 2, que es el elemento de origen.
  4. Evento de propagación MouseDown en el elemento de hoja n.º 2, que es el elemento de origen.
  5. Evento de propagación MouseDown en el elemento intermedio n.º 1.
  6. Evento de propagación MouseDown en el elemento raíz.

El delegado de controlador de eventos enrutados proporciona referencias al objeto que ha desencadenado el evento y al objeto en el que se ha invocado el controlador. La propiedad Source de los datos del evento informa sobre el objeto que generó originalmente el evento. El parámetro sender informa sobre el objeto en el que se ha invocado el controlador. Para cualquier instancia de evento enrutado dada, el objeto que generó el evento no cambia a medida que el evento viaja a través del árbol de elementos, pero sender sí. En los pasos 3 y 4 del diagrama anterior, Source y sender son el mismo objeto.

Si el controlador de eventos de entrada completa la lógica específica de la aplicación necesaria para abordar el evento, debe marcar el evento de entrada como controlado. Normalmente, una vez que un evento de entrada se marca como Handled, no se invocan controladores más adelante en la ruta de eventos. Sin embargo, los controladores de eventos de entrada registrados con el parámetro handledEventsToo establecido en true se invocarán incluso cuando el evento se marque como controlado. Para más información, consulte Eventos de vista previa o Marcar eventos enrutados como controlados y control de clases.

El concepto de pares de eventos de vista previa y propagación, con datos de evento compartido y el desencadenamiento secuencial del evento de vista previa y, a continuación, el de propagación, solo se aplica a algunos eventos de entrada de WPF y no a todos los eventos enrutados. Si implementa su propio evento de entrada para abordar un escenario avanzado, considere la posibilidad de seguir el enfoque de par de eventos de entrada de WPF.

Si va a implementar su propio control compuesto que responde a eventos de entrada, considere la posibilidad de usar eventos de vista previa para suprimir y reemplazar los eventos de entrada generados en subcomponentes con un evento de nivel superior que representa el control completo. Para más información, consulte Marcar eventos enrutados como controlados y control de clases.

Para obtener más información sobre el sistema de entrada de WPF y cómo interactúan las entradas y los eventos en escenarios de aplicación típicos, consulte Información general sobre entrada.

EventSetters y EventTriggers

En los estilos de marcado, se puede incluir la sintaxis predeclarada de control de eventos de XAML mediante un elemento EventSetter. Cuando se procesa el XAML, el controlador al que se hace referencia se agrega a la instancia que recibe el estilo. Solo puede declarar un EventSetter para un evento enrutado. En el ejemplo siguiente, el método de control de eventos ApplyButtonStyle al que se hace referencia se implementa en el código subyacente.

<StackPanel>
    <StackPanel.Resources>
        <Style TargetType="{x:Type Button}">
            <EventSetter Event="Click" Handler="ApplyButtonStyle"/>
        </Style>
    </StackPanel.Resources>
    <Button>Click me</Button>
    <Button Click="Button_Click">Click me</Button>
</StackPanel>

Es probable que el nodo Style ya contenga otra información de estilo que pertenezca a los controles del tipo especificado y que el hecho de que EventSetter forme parte de esos estilos promueva la reutilización del código incluso en el nivel de marcado. Además, un EventSetter elimina los nombres de método para los controladores de la aplicación general y el marcado de página.

Otra sintaxis especializada que combina el evento enrutado con características de animación de WPF es EventTrigger. Al igual que con EventSetter, solo puede declarar un elemento EventTrigger para un evento enrutado. Normalmente, un EventTrigger se declara como parte de un estilo, pero un EventTrigger también puede declararse en elementos de nivel de página como parte de la colección Triggers, o en una ControlTemplate. Un EventTrigger permite especificar una Storyboard que se ejecuta cada vez que un evento enrutado llega a un elemento de su ruta que declara un EventTrigger para ese evento. La ventaja de un EventTrigger frente a simplemente controlar el evento y hacer que inicie un guión gráfico existente es que un EventTrigger proporciona un mejor control sobre el guión gráfico y su comportamiento en tiempo de ejecución. Para más información, consulte Cómo: Utilizar desencadenadores de eventos para controlar un guión gráfico después de su inicio.

Más información sobre los eventos enrutados

Puede usar los conceptos e instrucciones de este artículo como punto de partida al crear eventos enrutados personalizados en sus propias clases. También puede admitir los eventos personalizados con clases y delegados de datos de evento especializados. El propietario de un evento enrutado puede ser cualquier clase, pero los eventos enrutados deben desencadenarlos y controlarlos las clases derivadas UIElement o ContentElement para que sean útiles. Para más información sobre los eventos personalizados, consulte Crear un evento enrutado personalizado.

Vea también