Compartir a través de


Automatización de la interfaz de usuario de un control personalizado de WPF

Actualización: Julio de 2008

Automatización de la interfaz de usuario de Microsoft proporciona una única interfaz generalizada que los clientes de automatización pueden usar para examinar o utilizar las interfaces de usuario de diversas plataformas y marcos de trabajo. Automatización de la interfaz de usuario permite que código de control de calidad (prueba) y aplicaciones de accesibilidad como lectores de pantalla examinen los elementos de la interfaz de usuario y simulen la interacción del usuario con ellos desde otro código. Para obtener información sobre Automatización de la interfaz de usuario en todas las plataformas, vea Accesibilidad.

En este tema se explica cómo implementar un proveedor de automatización de la interfaz de usuario de servidor para un control personalizado que se ejecuta en una aplicación WPF. WPF admite Automatización de la interfaz de usuario a través de un árbol de objetos de automatización del mismo nivel correspondiente al árbol de elementos de interfaz de usuario. Las aplicaciones y el código de prueba que proporcionan características de accesibilidad pueden usar objetos de automatización del mismo nivel directamente (para código en proceso) o a través de la interfaz generalizada proporcionada por Automatización de la interfaz de usuario.

Este tema contiene las secciones siguientes.

  • Clases de automatización del mismo nivel
  • Clases de automatización del mismo nivel integradas
  • Consideraciones de seguridad para elementos del mismo nivel derivados
  • Navegación entre elementos del mismo nivel
  • Personalizaciones de un elemento del mismo nivel derivado
  • Temas relacionados

Clases de automatización del mismo nivel

Los controles de WPF admiten Automatización de la interfaz de usuario a través de un árbol de clases del mismo nivel que se derivan de AutomationPeer. Por convención, los nombres de las clases del mismo nivel empiezan por el nombre de la clase del control y terminan por "AutomationPeer". Por ejemplo, ButtonAutomationPeer es la clase del mismo nivel para la clase del control Button. Las clases del mismo nivel equivalen, en líneas generales, a los tipos de control de Automatización de la interfaz de usuario, pero son específicas de los elementos de WPF. El código de automatización que tiene acceso a las aplicaciones WPF a través de la interfaz de Automatización de la interfaz de usuario no utiliza los elementos de automatización del mismo nivel directamente, pero el código de automatización del mismo espacio de proceso sí puede hacerlo.

Clases de automatización del mismo nivel integradas

Los elementos implementan una clase de automatización del mismo nivel si aceptan actividad de la interfaz por parte del usuario o si contienen información necesaria para los usuarios de aplicaciones de lectores de pantalla. No todos los elementos visuales de WPF tienen objetos de automatización del mismo nivel. Algunos ejemplos de clases que implementan objetos de automatización del mismo nivel son Button, TextBox y Label. Ejemplos de clases que no implementan objetos de automatización del mismo nivel son las clases derivadas de Decorator, como Border, y las clases basadas en Panel, como Grid y Canvas.

La clase base Control no tiene una clase correspondiente del mismo nivel. Si necesita una clase correspondiente del mismo nivel para un control personalizado derivado de Control, debe derivar la clase personalizada del mismo nivel de FrameworkElementAutomationPeer.

Consideraciones de seguridad para elementos del mismo nivel derivados

Los elementos de automatización del mismo nivel deben ejecutarse en un entorno de confianza parcial. El código del ensamblado UIAutomationClient no está configurado para ejecutarse en un entorno de confianza parcial, por lo que el código de automatización de los elementos del mismo nivel no debe hacer referencia a dicho ensamblado. En su lugar, debería utilizar las clases del ensamblado UIAutomationTypes. Por ejemplo, debería utilizar la clase AutomationElementIdentifiers del ensamblado UIAutomationTypes, que corresponde a la clase AutomationElement del ensamblado UIAutomationClient. Es seguro hacer referencia al ensamblado UIAutomationTypes en el código de los elementos de automatización del mismo nivel.

Después de localizar los elementos de automatización del mismo nivel, el código en proceso puede navegar por el árbol de elementos del mismo nivel mediante los métodos GetChildren y GetParent del objeto. La navegación por los elementos de WPF dentro de un control se admite mediante la implementación en los elementos del mismo nivel del método GetChildrenCore. El sistema de automatización de la interfaz de usuario llama a este método para construir un árbol de elementos secundarios incluidos en un control; por ejemplo, los elementos de un cuadro de lista. El método UIElementAutomationPeer.GetChildrenCore predeterminado recorre el árbol visual de elementos para generar el árbol de objetos de automatización del mismo nivel. Los controles personalizados invalidan este método para exponer los elementos secundarios a los clientes de automatización y devuelven los objetos de automatización del mismo nivel de los elementos que transmiten información o permiten la interacción con el usuario.

Personalizaciones de un elemento del mismo nivel derivado

Todas las clases derivadas de UIElement y ContentElement contienen el método virtual protegido OnCreateAutomationPeer. WPF llama a OnCreateAutomationPeer para obtener el objeto de automatización del mismo nivel correspondiente a cada control. El código de automatización puede utilizar los elementos del mismo nivel para obtener información sobre las características de un control y simular el uso interactivo. Un control personalizado que admita la automatización debe invalidar OnCreateAutomationPeer y devolver una instancia de una clase derivada de AutomationPeer. Por ejemplo, si un control personalizado se deriva de la clase ButtonBase, el objeto devuelto por OnCreateAutomationPeer debería derivarse de ButtonBaseAutomationPeer.

Al implementar un control personalizado, debe invalidar los métodos básicos ("Core") de la clase base de automatización del mismo nivel que describen el comportamiento único y específico del control personalizado.

Invalidar OnCreateAutomationPeer

Invalide el método OnCreateAutomationPeer para el control personalizado, de modo que devuelva el objeto de proveedor, que debe derivarse directa o indirectamente de AutomationPeer.

Invalidar GetPattern

Aunque los elementos de automatización del mismo nivel simplifican algunos aspectos de la implementación de proveedores de Automatización de la interfaz de usuario de servidor, los elementos de automatización del mismo nivel de controles personalizados aún tienen que controlar las interfaces patrón. Al igual que los proveedores que no son de WPF, los elementos del mismo nivel admiten patrones de controles mediante implementaciones de interfaces en el espacio de nombres System.Windows.Automation.Provider, como IInvokeProvider. Las interfaces de patrones de controles las puede implementar el propio elemento del mismo nivel u otro objeto. La implementación en el elemento del mismo nivel de GetPattern devuelve el objeto que admite el patrón especificado. El código de Automatización de la interfaz de usuario llama al método GetPattern y especifica un valor de enumeración de PatternInterface. El método que invalida GetPattern debería devolver el objeto que implementa el patrón especificado. Si el control no tiene ninguna implementación personalizada de un patrón, puede llamar a la implementación del tipo base de GetPattern para recuperar su implementación o un valor null si no se admite el patrón para este tipo de control. Por ejemplo, un control NumericUpDown personalizado puede establecerse en un valor dentro de un intervalo, de forma que su elemento de Automatización de la interfaz de usuario del mismo nivel implemente la interfaz IRangeValueProvider. En el ejemplo siguiente se muestra cómo se invalida el método GetPattern del elemento del mismo nivel para responder a un valor PatternInterface.RangeValue.

public override object GetPattern(PatternInterface patternInterface)
{
    if (patternInterface == PatternInterface.RangeValue)
    {
        return this;
    }
    return base.GetPattern(patternInterface);
}

Un método GetPattern también puede especificar un subelemento como proveedor del patrón. El siguiente código muestra cómo ItemsControl transfiere el control del patrón de desplazamiento al elemento del mismo nivel de su control ScrollViewer interno.

public override object GetPattern(PatternInterface patternInterface)
{
    if (patternInterface == PatternInterface.Scroll)
    {
        ItemsControl owner = (ItemsControl) base.Owner;

        // ScrollHost is internal to the ItemsControl class
        if (owner.ScrollHost != null)
        {
            AutomationPeer peer = UIElementAutomationPeer.CreatePeerForElement(owner.ScrollHost);
            if ((peer != null) && (peer is IScrollProvider))
            {
                peer.EventsSource = this;
                return (IScrollProvider) peer;
            }
        }
    }
    return base.GetPattern(patternInterface);
}

Para especificar un subelemento para el control del patrón, este código obtiene el objeto del subelemento, crea un elemento del mismo nivel mediante el método CreatePeerForElement, establece como valor de la propiedad EventsSource del nuevo elemento del mismo nivel el elemento actual y devuelve el nuevo elemento del mismo nivel. Al establecer EventsSource en un subelemento se impide que el subelemento aparezca en el árbol de objetos automatización del mismo nivel y se designa que todos los eventos provocados por el subelemento se originan en el control especificado en EventsSource. El control ScrollViewer no aparece en el árbol de automatización y los eventos de desplazamiento que genera parecerán originarse en el objeto ItemsControl.

Invalidar métodos básicos ("Core")

El código de automatización obtiene información sobre el control llamando a los métodos públicos de la clase del mismo nivel. Para proporcionar información sobre el control, invalide los métodos cuyo nombre finalice en "Core" cuando la implementación del control difiera de la proporcionada por la clase base de automatización del mismo nivel. Como mínimo, el control debe implementar los métodos GetClassNameCore y GetAutomationControlTypeCore, como se muestra en el ejemplo siguiente.

protected override string GetClassNameCore()
{
    return "NumericUpDown";
}

protected override AutomationControlType GetAutomationControlTypeCore()
{
    return AutomationControlType.Spinner;
}

La implementación que realice de GetAutomationControlTypeCore describe el control mediante la devolución de un valor ControlType. Aunque puede devolver ControlType.Custom, debería devolver un tipo de control más específico (si describe el control con precisión). Un valor devuelto de ControlType.Custom requiere trabajo adicional para que el proveedor implemente Automatización de la interfaz de usuario, y los productos cliente de Automatización de la interfaz de usuario no pueden prever la estructura del control, la interacción con el teclado y los posibles patrones del control.

Implemente los métodos IsControlElementCore y IsContentElementCore para indicar si el control contiene datos o desempeña una función de interacción en la interfaz de usuario (o ambas cosas). De forma predeterminada, ambos métodos devuelven true. Esta configuración aumenta la facilidad de uso de herramientas de automatización como los lectores de pantalla, que pueden utilizar estos métodos para filtrar el árbol de automatización. Si su método GetPattern transfiere el control del patrón a un subelemento del mismo nivel, el método IsControlElementCore de dicho subelemento puede devolver el valor false a fin de ocultar el subelemento del mismo nivel del árbol de automatización. Por ejemplo, el desplazamiento por un control ListBox se controla mediante un elemento ScrollViewer, y los objetos de automatización del mismo nivel para PatternInterface.Scroll los devuelve el método GetPattern del elemento ScrollViewerAutomationPeer asociado a ListBoxAutomationPeer. Por lo tanto, el método IsControlElementCore de ScrollViewerAutomationPeer devuelve false, de forma que ScrollViewerAutomationPeer no aparece en el árbol de automatización.

Los elementos de automatización del mismo nivel deben proporcionar los valores predeterminados adecuados para el control. Tenga en cuenta que el código XAML que hace referencia al control puede invalidar sus implementaciones del mismo nivel de métodos básicos (core) mediante la inclusión de atributos AutomationProperties. Por ejemplo, el siguiente código XAML crea un botón que tiene dos propiedades personalizadas de Automatización de la interfaz de usuario.

<Button AutomationProperties.Name="Special" 
    AutomationProperties.HelpText="This is a special button."/>

Implementar proveedores de patrón

Las interfaces implementadas por un proveedor personalizado se declaran explícitamente si el elemento propietario se deriva directamente de Control. Por ejemplo, en el código siguiente se declara un elemento del mismo nivel para Control que implementa un valor de intervalo.

public class RangePeer1 : FrameworkElementAutomationPeer, IRangeValueProvider { }

Si el control propietario se deriva de un tipo específico de control, como RangeBase, el elemento del mismo nivel puede derivarse de una clase del mismo nivel derivada equivalente. En este caso, el elemento del mismo nivel se derivaría de RangeBaseAutomationPeer, que proporciona una implementación base de IRangeValueProvider. En el código siguiente se muestra la declaración de este elemento del mismo nivel.

public class RangePeer2 : RangeBaseAutomationPeer { }

Para obtener un ejemplo de implementación, vea Ejemplo NumericUpDown Custom Control with Theme and UI Automation Support.

Provocar eventos

Los clientes de automatización pueden suscribirse a eventos de automatización. Los controles personalizados deben notificar los cambios de estado del control mediante llamadas al método RaiseAutomationEvent. Del mismo modo, cuando cambie el valor de una propiedad, llame al método RaisePropertyChangedEvent. El código siguiente muestra cómo obtener el objeto del mismo nivel a partir del código del control y cómo llamar a un método para provocar un evento. Como medida de optimización, el código determina si hay agentes de escucha para este tipo de evento. Provocar el evento solamente cuando haya agentes de escucha evita una sobrecarga innecesaria y ayuda a que el control siga respondiendo.

if (AutomationPeer.ListenerExists(AutomationEvents.PropertyChanged))
{
    NumericUpDownAutomationPeer peer = 
        UIElementAutomationPeer.FromElement(nudCtrl) as NumericUpDownAutomationPeer;

    if (peer != null)
    {
        peer.RaisePropertyChangedEvent(
            RangeValuePatternIdentifiers.ValueProperty,
            (double)oldValue,
            (double)newValue);
    }
}

Vea también

Tareas

Ejemplo NumericUpDown Custom Control with Theme and UI Automation Support

Ejemplo Test Script Generator

Conceptos

Información general sobre UI Automation

Implementación del proveedor de UI Automation en el servidor

Historial de cambios

Fecha

Historial

Motivo

Julio de 2008

Se ha agregado un tema.

Mejora de la información.