Introducción a eventos y eventos enrutados

API importantes

Describimos el concepto de programación de eventos en una aplicación de Windows Runtime cuando se usa C#, Visual Basic o extensiones del componente Visual C++ (C++/CX) como lenguaje de programación y XAML para la definición de la UI. Puedes asignar controladores para eventos como parte de las declaraciones de los elementos de la interfaz de usuario en XAML, o puedes agregar los controladores en el código. Windows Runtime admite eventos enrutados, lo que implica que ciertos eventos de entrada y eventos de datos puedan ser controlados por otros objetos distintos del objeto que originó el evento. Los eventos enrutados son útiles cuando tienes que definir plantillas de control o usar contenedores de páginas o de diseño.

Eventos como concepto de programación

Por lo general, los conceptos de eventos al programar una aplicación de Windows Runtime son similares al modelo de eventos en los lenguajes de programación más populares. Si ya sabes cómo trabajar con eventos de Microsoft .NET o C++, entonces empiezas con ventaja. Sin embargo, no es necesario saber mucho sobre los conceptos del modelo de eventos para realizar algunas tareas básicas, como adjuntar controladores.

Al usar C#, Visual Basic o C++/CX como lenguaje de programación, la UI se define en el marcado (XAML). En la sintaxis de marcado XAML, algunos de los principios de conexión de eventos entre elementos de marcado y entidades de código en tiempo de ejecución son similares a otras tecnologías web, como ASP.NET o HTML5.

Nota El código que proporciona la lógica en tiempo de ejecución para una UI definida por XAML se conoce a menudo como código subyacente o el archivo de código subyacente. En las vistas de la solución de Microsoft Visual Studio, esta relación se muestra gráficamente, donde el archivo de código subyacente es un archivo dependiente y anidado frente a la página XAML a la que hace referencia.

Button.Click: introducción a eventos y XAML

Una de las tareas de programación más comunes para una aplicación de Windows Runtime es capturar la entrada de usuario en la UI. Por ejemplo, la UI podría tener un botón en el que el usuario debe hacer clic para enviar información o cambiar el estado.

La UI de la aplicación de Windows Runtime se define mediante la generación de XAML. Este XAML suele ser la salida de una superficie de diseño en Visual Studio. También puedes escribir el XAML en un editor de texto sin formato o en un editor XAML de terceros. Al generar ese XAML, puedes conectar controladores de eventos para elementos de UI individuales al mismo tiempo que defines todos los demás atributos XAML que establecen valores de propiedad de ese elemento de UI.

Para conectar los eventos en XAML, especifica el nombre en forma de cadena del método de controlador que ya has definido o definirás más adelante en el código subyacente. Por ejemplo, este XAML define un objeto Button con otras propiedades (atributo x:Name, Content) asignadas como atributos y conecta un controlador para el evento Click del botón haciendo referencia a un método denominado ShowUpdatesButton_Click:

<Button x:Name="showUpdatesButton"
  Content="{Binding ShowUpdatesText}"
  Click="ShowUpdatesButton_Click"/>

SugerenciaConexión de eventos es un término de programación. Hace referencia al proceso o código en el que se indica que las ocurrencias de un evento deben invocar un método de controlador con nombre. En la mayoría de los modelos de código de procedimientos, la conexión de eventos es código implícito o explícito "AddHandler" que asigna nombres tanto al evento como al método, y normalmente implica una instancia de objeto de destino. En XAML, "AddHandler" es implícito y la conexión de eventos consiste en asignar un nombre completo al evento como el nombre de atributo de un elemento de objeto y asignar un nombre al controlador como valor de ese atributo.

Escribe el controlador real en el lenguaje de programación que estás usando para todo el código y el código subyacente de la app. Con el atributo Click="ShowUpdatesButton_Click", has creado un contrato para que cuando la XAML se compile y analice, tanto el paso de compilación de la XAML en la acción de compilación de tu IDE como el análisis final de la XAML cuando se cargue la aplicación puedan encontrar un método nombrado ShowUpdatesButton_Click como parte del código de la aplicación. ShowUpdatesButton_Click debe ser un método que implemente una firma de método compatible (basada en un delegado) para cualquier controlador del evento Click. Por ejemplo, este código define el controlador ShowUpdatesButton_Click.

private void ShowUpdatesButton_Click (object sender, RoutedEventArgs e) 
{
    Button b = sender as Button;
    //more logic to do here...
}
Private Sub ShowUpdatesButton_Click(ByVal sender As Object, ByVal e As RoutedEventArgs)
    Dim b As Button = CType(sender, Button)
    '  more logic to do here...
End Sub
void winrt::MyNamespace::implementation::BlankPage::ShowUpdatesButton_Click(Windows::Foundation::IInspectable const& sender, Windows::UI::Xaml::RoutedEventArgs const& e)
{
    auto b{ sender.as<Windows::UI::Xaml::Controls::Button>() };
    // More logic to do here.
}
void MyNamespace::BlankPage::ShowUpdatesButton_Click(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e) 
{
    Button^ b = (Button^) sender;
    //more logic to do here...
}

En este ejemplo, el método ShowUpdatesButton_Click se basa en el delegado RoutedEventHandler. Sabrás que este es el delegado que se va a usar porque verás ese delegado denominado en la sintaxis del método Click.

Sugerencia Visual Studio proporciona una manera cómoda de asignar un nombre al controlador de eventos y definir el método de controlador mientras editas XAML. Al proporcionar el nombre de atributo del evento en el editor de texto XAML, espera un momento hasta que se muestre una lista de Microsoft IntelliSense. Si haces clic en <Nuevo controlador de eventos> en la lista, Microsoft Visual Studio sugerirá un nombre de método basado en el x:Name (o nombre de tipo) del elemento, el nombre del evento y un sufijo numérico. A continuación, puedes hacer clic con el botón derecho en el nombre del controlador de eventos seleccionado y hacer clic en Navegar al controlador de eventos. De esta forma, accederás directamente a la definición del controlador de eventos recién insertado, como se muestra en la vista del editor de código del archivo de código subyacente para la página XAML. El controlador de eventos ya tiene la firma correcta, incluido el parámetro sender y la clase de datos de eventos que usa el evento. Además, si ya existe un método de controlador con la firma correcta en el código subyacente, el nombre del método aparece en la lista desplegable Autocompletar junto con la opción <Nuevo controlador de eventos>. También puedes presionar la tecla TAB como acceso directo en lugar de hacer clic en los elementos de lista de IntelliSense.

Definición de controladores de eventos

Para los objetos que son elementos de la UI y declarados en XAML, el código del controlador de eventos se define en la clase parcial que actúa como código subyacente para una página XAML. Los controladores de eventos son métodos que escribes como parte de la clase parcial asociada a tu XAML. Estos controladores de eventos se basan en los delegados que usa un evento determinado. Los métodos del controlador de eventos pueden ser públicos o privados. El acceso privado funciona porque el controlador y la instancia creados por el XAML se unen en última instancia mediante la generación de código. En general, se recomienda que los métodos del controlador de eventos sean privados en la clase.

Nota Los controladores de eventos para C++ no se definen en clases parciales, se declaran en el encabezado como miembro de clase privado. Las acciones de compilación de un proyecto de C++ se encargan de generar código que admita el sistema de tipos XAML y el modelo de código subyacente para C++.

Parámetro sender y datos de eventos

El controlador que escriba para el evento puede tener acceso a dos valores que están disponibles como entrada para cada caso en el que se invoca el controlador. El primero es sender, que es una referencia al objeto donde se adjunta el controlador. El parámetro sender se escribe como el tipo Object básico. Una técnica común consiste en convertir sender a un tipo más preciso. Esta técnica es útil si esperas comprobar o cambiar el estado en el propio objeto sender. En función del diseño de tu aplicación, normalmente conoces un tipo al que es seguro transmitir el contenido sender, según la ubicación en la que se adjuntó el controlador u otros datos específicos de diseño.

El segundo valor son los datos del evento, que generalmente aparecen en las definiciones de sintaxis como el parámetro e. Puedes detectar qué propiedades de los datos de eventos están disponibles examinando el parámetro e del delegado asignado para el evento específico que estás controlando y, a continuación, mediante IntelliSense o el Examinador de objetos en Visual Studio. O bien, puedes usar la documentación de referencia de Windows Runtime.

Para algunos eventos, los valores de propiedad específicos de los datos del evento son tan importantes como saber que se produjo el evento. Esto es especialmente cierto de los eventos de entrada. En el caso de los eventos de puntero, la posición del puntero cuando se produjo el evento podría ser importante. Para los eventos de teclado, todas las teclas posibles activan un evento KeyDown y KeyUp. Para determinar qué tecla ha presionado un usuario, debes tener acceso a KeyRoutedEventArgs, que está disponible para el controlador de eventos. Para obtener más información sobre cómo gestionar eventos de entrada, consulta Interacciones de teclado y Controlar la entrada del puntero. Los eventos de entrada y los escenarios de entrada suelen tener consideraciones adicionales que no se tratan en este tema, como la captura de puntero para eventos de puntero y las teclas modificadoras y los códigos de clave de plataforma para eventos de teclado.

Controladores de eventos que usan el patrón asincrónico

En algunos casos, querrás usar las API que usan un patrón async dentro de un controlador de eventos. Por ejemplo, puedes usar un botón en una barra de aplicaciones para mostrar un selector de archivos e interactuar con él. Sin embargo, muchas de las API del selector de archivos son asíncronas. Deben llamarse dentro de un ámbito async/awaitable, y el compilador lo hará cumplir. Por lo tanto, lo que puedes hacer es agregar la palabra clave async al controlador de eventos de forma que el controlador ahora sea asyncvoid. Ahora el controlador de eventos puede realizar llamadas async/awaitable.

Para obtener un ejemplo de control de eventos de interacción del usuario mediante el patrón async, consulta Acceso a archivos y selectores (parte de la serie Crear tu primera aplicación de Windows Runtime con C# o Visual Basic). Consulte también [Llamar a API asincrónicas en C).

Adición de controladores de eventos en el código

XAML no es la única manera de asignar un controlador de eventos a un objeto. Para agregar controladores de eventos a cualquier objeto determinado en el código, incluidos los objetos que no se pueden usar en XAML, puedes usar la sintaxis específica del idioma para agregar controladores de eventos.

En C#, la sintaxis es usar el operador +=. Para registrar el controlador, haz referencia al nombre del método del controlador de eventos en el lado derecho del operador.

Si usas código para agregar controladores de eventos a objetos que aparecen en la interfaz de usuario en tiempo de ejecución, una práctica habitual es agregar estos controladores en respuesta a un evento de duración de objeto o devolución de llamada, como Loaded o OnApplyTemplate, de modo que los controladores de eventos del objeto pertinente estén listos para eventos iniciados por el usuario en tiempo de ejecución. En este ejemplo se muestra un esquema XAML de la estructura de página y, a continuación, se proporciona la sintaxis del lenguaje C# para agregar un controlador de eventos a un objeto.

<Grid x:Name="LayoutRoot" Loaded="LayoutRoot_Loaded">
  <StackPanel>
    <TextBlock Name="textBlock1">Put the pointer over this text</TextBlock>
...
  </StackPanel>
</Grid>
void LayoutRoot_Loaded(object sender, RoutedEventArgs e)
{
    textBlock1.PointerEntered += textBlock1_PointerEntered;
    textBlock1.PointerExited += textBlock1_PointerExited;
}

Nota Existe una sintaxis más detallada. En 2005, C# agregó una característica denominada inferencia de delegado, que permite a un compilador deducir la nueva instancia de delegado y habilita la sintaxis anterior y más sencilla. La sintaxis detallada es funcionalmente idéntica al ejemplo anterior, pero crea explícitamente una nueva instancia de delegado antes de registrarla, por lo que no aprovecha la inferencia de delegados. Esta sintaxis explícita es menos común, pero es posible que todavía la veas en algunos ejemplos de código.

void LayoutRoot_Loaded(object sender, RoutedEventArgs e)
{
    textBlock1.PointerEntered += new PointerEventHandler(textBlock1_PointerEntered);
    textBlock1.PointerExited += new MouseEventHandler(textBlock1_PointerExited);
}

Hay dos posibilidades para la sintaxis de Visual Basic. Una es paralelizar la sintaxis de C# y adjuntar controladores directamente a las instancias. Esto requiere la palabra clave AddHandler y también el operador AddressOf que desreferencia el nombre del método del controlador.

La otra opción para la sintaxis de Visual Basic es usar la palabra clave Handles en controladores de eventos. Esta técnica es adecuada para los casos en los que se espera que existan controladores en objetos en tiempo de carga y persistan durante toda la duración del objeto. El uso de Handles en un objeto definido en XAML requiere que proporciones un Name / x:Name. Este nombre se convierte en el calificador de instancia necesario para la parte Instance.Event de la sintaxis Handles. En este caso, no necesitas un controlador de eventos basado en la duración del objeto para iniciar la asociación de los otros controladores de eventos; las conexiones Handles se crean al compilar la página XAML.

Private Sub textBlock1_PointerEntered(ByVal sender As Object, ByVal e As PointerRoutedEventArgs) Handles textBlock1.PointerEntered
' ...
End Sub

Nota Visual Studio y su superficie de diseño XAML generalmente promueven la técnica de control de instancias en lugar de la palabra clave Handles. Esto se debe a que establecer la conexión del controlador de eventos en XAML forma parte del flujo de trabajo típico del desarrollador del diseñador y la técnica de palabra clave Handles no es compatible con la conexión de los controladores de eventos en XAML.

En C++/CX, también se usa la sintaxis +=, pero hay diferencias con respecto al formulario básico de C#:

  • No existe ninguna inferencia de delegado, por lo que debes usar ref new para la instancia de delegado.
  • El constructor delegado tiene dos parámetros y requiere el objeto de destino como primer parámetro. Normalmente, se especifica esto.
  • El constructor delegado requiere la dirección del método como segundo parámetro, por lo que el operador de referencia & precede al nombre del método.
textBlock1().PointerEntered({this, &MainPage::TextBlock1_PointerEntered });
textBlock1->PointerEntered += 
ref new PointerEventHandler(this, &BlankPage::textBlock1_PointerEntered);

Eliminación de controladores de eventos en el código

Normalmente no es necesario eliminar controladores de eventos en el código, incluso si los agregaste en el código. El comportamiento de duración del objeto para la mayoría de los objetos de Windows Runtime, como páginas y controles, destruirá los objetos cuando se desconecten de la ventana principal y su árbol visual, y las referencias de delegado también se destruyen. .NET lo hace a través de la recolección de elementos no utilizados y Windows Runtime con C++/CX usa referencias débiles de manera predeterminada.

Hay algunos casos raros en los que se desea eliminar explícitamente los manejadores de eventos. Entre ellas se incluyen las siguientes:

  • Controladores que has agregado para eventos estáticos, que no se pueden recopilar de forma convencional. Algunos ejemplos de eventos estáticos en la API de Windows Runtime son los eventos de las clases CompositionTarget y Clipboard.
  • Prueba el código en el que deseas que el tiempo de eliminación del controlador sea inmediato o el código en el que se intercambian controladores de eventos antiguos o nuevos para un evento en tiempo de ejecución.
  • Implementación de un descriptor de acceso remove personalizado.
  • Eventos estáticos personalizados.
  • Controladores para las navegaciones de página.

FrameworkElement.Unloaded o Page.NavigatedFrom son posibles desencadenadores de eventos que tienen posiciones adecuadas en la administración de estado y la duración de objetos, de modo que puedes usarlos para quitar controladores para otros eventos.

Por ejemplo, puedes quitar un controlador de eventos denominado textBlock1_PointerEntered del objeto de destino textBlock1 mediante este código.

textBlock1.PointerEntered -= textBlock1_PointerEntered;
RemoveHandler textBlock1.PointerEntered, AddressOf textBlock1_PointerEntered

También puedes quitar controladores para los casos en los que el evento se agregó a través de un atributo XAML, lo que significa que el controlador se agregó en el código generado. Esto es más fácil si proporcionaste un valor Name para el elemento en el que se adjuntó el controlador, ya que proporciona una referencia de objeto para el código más adelante; sin embargo, también puedes recorrer el árbol de objetos para encontrar la referencia de objeto necesaria en los casos en los que el objeto no tiene Name.

Si necesitas eliminar un controlador de eventos en C++/CX, necesitarás un token de registro, que deberías haber recibido del valor de retorno del registro del manejador de eventos +=. Esto se debe a que el valor que se utiliza para el lado derecho de la cancelación de registro -= en la sintaxis C++/CX es el token, no el nombre del método. Para C++/CX, no se pueden eliminar los controladores que se añadieron como un atributo XAML porque el código generado por C++/CX no guarda un token.

Eventos enrutados

El tiempo de ejecución de Windows con C#, Microsoft Visual Basic o C++/CX admite el concepto de evento enrutado para un conjunto de eventos que están presentes en la mayoría de los elementos de la interfaz de usuario. Estos eventos son para escenarios de entrada e interacción con el usuario, y se implementan en la clase base UIElement. Aquí te dejamos una lista de eventos de entrada que son eventos enrutados:

Un evento enrutado es un evento que es potencialmente pasado (enrutado) de un objeto secundario a cada uno de sus sucesivos objetos primarios en un árbol de objetos. La estructura XAML de tu UI se aproxima a este árbol, cuya raíz es el elemento raíz en XAML. El verdadero árbol de objetos puede variar algo del anidamiento de elementos XAML, porque el árbol de objetos no incluye características del lenguaje XAML como las etiquetas de elementos de propiedad. Puedes concebir el evento enrutado como una propagación desde cualquier elemento secundario de un elemento objeto XAML que dispara el evento, hacia el elemento objeto primario que lo contiene. El evento y sus datos de evento pueden ser manejados en múltiples objetos a lo largo de la ruta del evento. Si ningún elemento tiene manejadores, la ruta continúa potencialmente hasta que se alcanza el elemento raíz.

Si conoces tecnologías web como el HTML dinámico (DHTML) o HTML5, es posible que ya estés familiarizado con el concepto de evento de propagación.

Cuando un evento enrutado se propaga a través de su ruta de eventos, todos los controladores de eventos adjuntos acceden a una instancia compartida de datos de eventos. Por lo tanto, si alguno de los datos del evento se puede escribir mediante un controlador, cualquier cambio hecho a los datos del evento será pasado al siguiente controlador, y puede que ya no represente los datos originales del evento. Cuando un evento tiene un comportamiento de evento enrutado, la documentación de referencia incluirá observaciones u otras anotaciones sobre el comportamiento enrutado.

La propiedad OriginalSource de RoutedEventArgs

Cuando un evento se propaga por una ruta de eventos, el sender ya no es el mismo objeto que el objeto que genera el evento. En su lugar, el sender es el objeto al que se adjunta el controlador que se está invocando.

En algunos casos, el sender no es interesante, y en su lugar le interesa información como sobre cuál de los posibles objetos secundario se encuentra el puntero cuando se dispara un evento de puntero, o qué objeto de una interfaz de usuario más grande tiene el foco cuando un usuario pulsa una tecla del teclado. Para estos casos, puede utilizar el valor de la propiedad OriginalSource. En todos los puntos de la ruta, OriginalSource informa del objeto original que disparó el evento, en lugar del objeto al que se adjunta el manejador. Sin embargo, para los eventos de entrada UIElement, ese objeto original es a menudo un objeto que no es inmediatamente visible en la XAML de definición de UI a nivel de página. En su lugar, ese objeto de origen original podría ser una parte templada de un control. Por ejemplo, si el usuario pasa el puntero por encima del borde de un Button, en la mayoría de los eventos de puntero el OriginalSource es una plantilla Border en la Template, no en el propio Button.

Sugerencia La propagación de eventos de entrada es especialmente útil si está creando un control basado en modelo. Cualquier control que tenga una plantilla puede tener una nueva plantilla aplicada por su consumidor. El consumidor que está tratando de recrear una plantilla de trabajo podría eliminar involuntariamente algún manejo de eventos declarado en la plantilla predeterminada. Todavía puede proporcionar el manejo de eventos a nivel de control adjuntando controladores como parte de la anulación OnApplyTemplate en la definición de la clase. A continuación, puede capturar los eventos de entrada que se propagan hasta la raíz del control en la instanciación.

La propiedad Handled

Varias clases de datos de eventos para eventos enrutados específicos contienen una propiedad llamada Handled. Para ver ejemplos, consulta PointerRoutedEventArgs.Handled, KeyRoutedEventArgs.Handled, DragEventArgs.Handled. En todos los casos Handled es una propiedad booleana configurable.

Establecer la propiedad Handled en true influye en el comportamiento del sistema de eventos. Cuando Handled es true, el enrutamiento se detiene para la mayoría de los controladores de eventos; el evento no continúa a lo largo de la ruta para notificar a otros controladores adjuntos de ese caso de evento en particular. Tú decides qué significa "handled" en el contexto del evento y cómo responde a ella tu aplicación. Básicamente, Handled es un protocolo simple que permite al código de la aplicación indicar que la ocurrencia de un evento no necesita propagarse a ningún contenedor, la lógica de tu aplicación se ha encargado de lo que se necesita hacer. A la inversa, sin embargo, tienes que tener cuidado de que no estás controlando eventos que probablemente deberían propagarse para que el sistema incorporado o los comportamientos de control puedan actuar. Por ejemplo, manejar eventos de bajo nivel dentro de las partes o elementos de un control de selección puede ser perjudicial. El control de selección podría estar buscando eventos de entrada para saber que la selección debe cambiar.

No todos los eventos enrutados pueden cancelar una ruta de esta forma, y puedes saberlo porque no tendrán una propiedad Handled. Por ejemplo, GotFocus y LostFocus sí que se propagan, pero siempre se propagan hasta la raíz, y sus clases de datos de eventos no tienen una propiedad Handled que pueda influir en ese comportamiento.

Controladores de eventos de entrada en los controles

Los controles específicos de Windows Runtime a veces utilizan internamente el concepto Handled para los eventos de entrada. Esto puede hacer que parezca que un evento de entrada nunca ocurre, porque tu código de usuario no puede controlarlo. Por ejemplo, la clase Button incluye lógica que maneja deliberadamente el evento de entrada general PointerPressed. Lo hace porque los botones disparan un evento Click que es iniciado por la entrada presionada por el puntero, así como por otros modos de entrada como el manejo de teclas como la tecla ENTRAR que puede invocar el botón cuando está enfocado. Para propósitos del diseño de la clase Button, el evento de entrada sin procesar es manejado conceptualmente, y los consumidores de la clase como tu código de usuario pueden en cambio interactuar con el evento Click relevante para el control. Los temas para clases de control específicas en la referencia de API de Windows Runtime a menudo indican el comportamiento de manejo de eventos que implementa la clase. En algunos casos, puedes cambiar el comportamiento anulando los métodos OnEvent. Por ejemplo, puedes cambiar la forma en que su clase derivada TextBox reacciona a la entrada de teclas anulando Control.OnKeyDown.

Registro de controladores de eventos enrutados ya gestionados

Anteriormente dijimos que establecer Handled en true evita llamar a la mayoría de los controladores. Sin embargo, el método AddHandler proporciona una técnica donde puedes adjuntar un controlador que siempre es invocado para la ruta, incluso si algún otro controlador anterior en la ruta ha establecido Handled en true en los datos de eventos compartidos. Esta técnica es útil si un control que está utilizando ha manejado el evento en su composición interna o para la lógica específica del control. Sin embargo, aún así se debe responder a él desde una instancia de control, o desde la interfaz de usuario de tu aplicación. Pero utilice esta técnica con precaución, porque puede contradecir el propósito de Handled y posiblemente romper las interacciones previstas de un control.

Solo los eventos enrutados que tienen un identificador de evento enrutado correspondiente pueden usar la técnica de control de eventos AddHandler, porque el identificador es una entrada requerida del método AddHandler. Consulte la documentación de referencia de AddHandler para obtener una lista de eventos que disponen de identificadores de eventos enrutados. En su mayor parte, se trata de la misma lista de eventos enrutados que te mostramos anteriormente. La excepción es que los dos últimos de la lista: GotFocus y LostFocus no tienen un identificador de evento enrutado, por lo que no puedes usar AddHandler para ellos.

Eventos enrutados fuera del árbol visual

Ciertos objetos participan en una relación con el árbol visual primario que conceptualmente es como tener una superposición sobre los visuales principales. Estos objetos no forman parte de las relaciones habituales primario-secundario que conectan todos los elementos del árbol con la raíz visual. Este es el caso de cualquier Popup o ToolTip que se muestre. Si quieres controlar eventos enrutados desde un Popup o ToolTip, coloca los controladores en elementos UI específicos que estén dentro del Popup o ToolTip y no en los elementos Popup o ToolTip mismos. No confíes en el enrutamiento dentro de cualquier composición que se realice para contenido Popup o ToolTip. Esto se debe a que el enrutamiento de eventos para eventos enrutados solo funciona a lo largo del árbol visual principal. Un Popup o ToolTip no se considera elemento primario de elementos UI subsidiarios y nunca recibe el evento enrutado, incluso si está intentando usar algo como el fondo predeterminado Popup como área de captura para eventos de entrada.

Pruebas de impacto y eventos de entrada

Determinar si un elemento de la interfaz de usuario es visible para el ratón, el tacto y el lápiz óptico, y en qué lugar, se denomina prueba de impacto. Para las acciones táctiles y también para los eventos específicos de interacción o manipulación que son consecuencias de una acción táctil, un elemento debe ser visible para ser el origen del evento y desencadenar el evento asociado a la acción. De lo contrario, la acción pasa el elemento a los elementos subyacentes o elementos primarios del árbol visual que podrían interactuar con esa entrada. Hay varios factores que afectan a las pruebas de posicionamiento, pero puede determinar si un elemento determinado puede desencadenar eventos de entrada comprobando su propiedad IsHitTestVisible. Esta propiedad devuelve true solo si el elemento cumple estos criterios:

  • El valor de la propiedad Visibility del elemento es Visible.
  • El valor de la propiedad Background o Fill del elemento no es null. Un valor nullBrush da como resultado la transparencia y la invisibilidad de la prueba de posicionamiento. (Para hacer que un elemento sea transparente, pero que también se pueda probar, usa un Pincel Transparent en lugar de null).

NotaBackground y Fill no están definidos por UIElement, sino que se definen por diferentes clases derivadas como Control y Shape. Pero las implicaciones de los pinceles que se usan para las propiedades en primer plano y en segundo plano son las mismas para las pruebas de posicionamiento y los eventos de entrada, independientemente de qué subclase implemente las propiedades.

  • Si el elemento es un control, su valor de propiedad IsEnabled debe ser true.
  • El elemento debe tener dimensiones reales en el diseño. Un elemento donde ActualHeight y ActualWidth son 0 no desencadenarán eventos de entrada.

Algunos controles tienen reglas especiales para las pruebas de posicionamiento. Por ejemplo, TextBlock no tiene ninguna propiedad Background, pero todavía se puede probar en toda la región de sus dimensiones. Los controles Image y MediaElement se pueden probar a través de sus dimensiones de rectángulo definidas, independientemente del contenido transparente, como el canal alfa en el archivo de origen multimedia que se muestra. Los controles WebView tienen un comportamiento especial de las pruebas de posicionamiento porque la entrada se puede controlar mediante los eventos de script de desencadenación y HTML hospedados.

La mayoría de las clases de Panel y Border no son comprobables en su propio fondo, pero todavía pueden controlar los eventos de entrada de usuario que se enrutan desde los elementos que contienen.

Puedes determinar qué elementos se encuentran en la misma posición que un evento de entrada de usuario, independientemente de si los elementos se pueden probar. Para ello, llama al método FindElementsInHostCoordinates. Como indica el nombre, este método busca los elementos en una ubicación relativa a un elemento host especificado. Sin embargo, las transformaciones aplicadas y los cambios de diseño pueden ajustar el sistema de coordenadas relativo de un elemento y, por tanto, afectar a qué elementos se encuentran en una ubicación determinada.

Comandos

Un pequeño número de elementos de la interfaz de usuario son compatibles con commanding. El comando usa eventos enrutados relacionados con la entrada en su implementación subyacente y permite el procesamiento de la entrada de interfaz de usuario relacionada (una determinada acción de puntero, una tecla de aceleración específica) invocando un único controlador de comandos. Si el comando está disponible para un elemento de interfaz de usuario, considera la posibilidad de usar sus API de comandos en lugar de cualquier evento de entrada discreto. Normalmente, se usa una referencia Binding en propiedades de una clase que define el modelo de vista para los datos. Las propiedades contienen comandos con nombre que implementan el patrón de comandos ICommand específico del idioma. Para obtener más información, consulta ButtonBase.Command.

Eventos personalizados en Windows Runtime

Para definir eventos personalizados, la forma de agregar el evento y lo que significa para el diseño de clase depende en gran medida del lenguaje de programación que use.

  • Para C# y Visual Basic, vas a definir un evento CLR. Puedes usar el patrón de eventos estándar de .NET, siempre que no uses descriptores de acceso personalizados (add/remove). Consejos adicionales:
  • Para C++/CX, consulta Eventos (C++/CX).
    • Usa referencias con nombre incluso para tus propios usos de eventos personalizados. No uses lambda para eventos personalizados, puedes crear una referencia circular.

No puedes declarar un evento enrutado personalizado para Windows Runtime; los eventos enrutados se limitan al conjunto que procede de Windows Runtime.

La definición de un evento personalizado normalmente se realiza como parte del ejercicio de definir un control personalizado. Es un patrón común tener una propiedad de dependencia que tenga una devolución de llamada modificada por propiedades y también definir un evento personalizado que se desencadena mediante la devolución de llamada de la propiedad de dependencia en algunos o todos los casos. Los consumidores de su control no tienen acceso a la llamada de retorno de cambio de propiedad que ha definido, pero tener un evento de notificación disponible es la mejor opción. Para más información, consulta Propiedades de dependencia personalizadas.