Compartilhar via


Automação de interface do usuário de um controle personalizado do WPF

A Automação da Interface do Usuário fornece uma interface única e generalizada que os clientes de automação podem usar para examinar ou operar as interfaces do usuário de uma variedade de plataformas e estruturas. A Automação da Interface do Usuário permite que o código de garantia de qualidade (teste) e os aplicativos de acessibilidade, como leitores de tela, examinem elementos da interface do usuário e simulem a interação do usuário com eles a partir de outro código. Para obter informações sobre a automação da interface do usuário em todas as plataformas, consulte Acessibilidade.

Este tópico descreve como implementar um provedor da Automação da Interface do Usuário do lado do servidor para um controle personalizado que é executado em um aplicativo WPF. O WPF oferece suporte à Automação da Interface do Usuário por meio de uma árvore de objetos de automação de mesmo nível paralela à árvore de elementos da interface do usuário. O código de teste e os aplicativos que fornecem recursos de acessibilidade podem usar objetos de mesmo nível de automação diretamente (para código em processo) ou por meio da interface generalizada fornecida pela Automação da Interface do Usuário.

Classes par de automação

Os controles WPF oferecem suporte à Automação da Interface do Usuário por meio de uma árvore de classes pares derivadas do AutomationPeer. Por convenção, os nomes de classe pares começam com o nome de classe do controle e terminam com "AutomationPeer". Por exemplo, ButtonAutomationPeer é a classe peer para a Button classe de controle. As classes peer são aproximadamente equivalentes aos tipos de controle de Automação da Interface do Usuário, mas são específicas para elementos WPF. O código de automação que acessa aplicativos WPF por meio da interface de automação da interface do usuário não usa pares de automação diretamente, mas o código de automação no mesmo espaço de processo pode usar pares de automação diretamente.

Classes pares de automação internas

Os elementos implementam uma classe par de automação se aceitam atividade proveniente da interface do usuário ou se contêm informações necessárias aos usuários de aplicativos de leitor de tela. Nem todos os elementos visuais do WPF têm pares de automação. Exemplos de classes que implementam pares de automação são Button, TextBoxe Label. Exemplos de classes que não implementam pares de automação são classes derivadas de Decorator, como , e classes baseadas em Panel, como BorderGrid e Canvas.

A classe base Control não tem uma classe de pares correspondente. Se você precisar de uma classe de mesmo nível para corresponder a um controle personalizado que deriva de , você deve derivar a classe de par personalizada de ControlFrameworkElementAutomationPeer.

Considerações de segurança para pares derivados

Os pares de automação devem ser executados em um ambiente de confiança parcial. O código no assembly UIAutomationClient não está configurado para ser executado em um ambiente de confiança parcial e o código par de automação não deve fazer referência a esse assembly. Em vez disso, você deve usar as classes no assembly UIAutomationTypes. Por exemplo, você deve usar a AutomationElementIdentifiers classe do assembly UIAutomationTypes, que corresponde à AutomationElement classe no assembly UIAutomationClient. É seguro fazer referência ao assembly UIAutomationTypes no código par de automação.

Navegação par

Depois de localizar um par de automação, o código em processo pode navegar na árvore de pares chamando os métodos GetChildren e GetParent os objetos do objeto. A navegação entre elementos WPF dentro de um controle é suportada pela implementação do GetChildrenCore método pelo peer. O sistema de Automação da Interface do Usuário chama esse método para compilar uma árvore de subelementos contidos em um controle, por exemplo, itens de lista em uma caixa de listagem. O método padrão UIElementAutomationPeer.GetChildrenCore atravessa a árvore visual de elementos para criar a árvore de pares de automação. Os controles personalizados substituem esse método para expor elementos filho a clientes de automação, retornando os pares de automação de elementos que transmitem informações ou permitem a interação do usuário.

Personalizações em um par derivado

Todas as classes que derivam UIElement de e ContentElement contêm o método OnCreateAutomationPeervirtual protegido . WPF chama OnCreateAutomationPeer para obter o objeto de peer de automação para cada controle. O código de automação pode usar o par para obter informações sobre recursos e características de um controle e para simular o uso interativo. Um controle personalizado que oferece suporte à automação deve substituir OnCreateAutomationPeer e retornar uma instância de uma classe derivada do AutomationPeer. Por exemplo, se um controle personalizado deriva ButtonBase da classe, o objeto retornado por OnCreateAutomationPeer deve derivar de ButtonBaseAutomationPeer.

Ao implementar um controle personalizado, você deve substituir os métodos "Principais" da classe base par de automação que descrevem o comportamento exclusivo e específico ao seu controle personalizado.

Substituir OnCreateAutomationPeer

Substitua o OnCreateAutomationPeer método para seu controle personalizado para que ele retorne seu objeto de provedor, que deve derivar direta ou indiretamente de AutomationPeer.

Substituir GetPattern

Os pares de automação simplificam alguns aspectos de implementação dos provedores de automação da interface do usuário do lado do servidor, mas os pares de automação de controle personalizados ainda devem lidar com interfaces de padrão. Como provedores não-WPF, os pares oferecem suporte a padrões de controle fornecendo implementações de interfaces no System.Windows.Automation.Provider namespace, como IInvokeProvider. As interfaces de padrão de controle podem ser implementadas pelo próprio par ou por outro objeto. A implementação do peer de GetPattern retorna o objeto que oferece suporte ao padrão especificado. O código de automação da interface do usuário chama o GetPattern método e especifica um valor de PatternInterface enumeração. Sua substituição de GetPattern deve retornar o objeto que implementa o padrão especificado. Se seu controle não tiver uma implementação personalizada de um padrão, você poderá chamar a implementação do tipo base de para recuperar sua implementação ou null se o padrão não tiver suporte para esse tipo de GetPattern controle. Por exemplo, um controle NumericUpDown personalizado pode ser definido como um valor dentro de um intervalo, para que seu par de automação da interface do usuário implemente a IRangeValueProvider interface. O exemplo a seguir mostra como o método do GetPattern par é substituído para responder a um PatternInterface.RangeValue valor.

public override object GetPattern(PatternInterface patternInterface)
{
    if (patternInterface == PatternInterface.RangeValue)
    {
        return this;
    }
    return base.GetPattern(patternInterface);
}
Public Overrides Function GetPattern(ByVal patternInterface As PatternInterface) As Object
    If patternInterface = PatternInterface.RangeValue Then
        Return Me
    End If
    Return MyBase.GetPattern(patternInterface)
End Function

Um GetPattern método também pode especificar um subelemento como um provedor de padrão. O código a seguir mostra como ItemsControl transfere a manipulação de padrão de rolagem para o par de seu controle interno ScrollViewer .

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);  
}  
Public Class Class1  
    Public Overrides Function GetPattern(ByVal patternInterface__1 As PatternInterface) As Object  
        If patternInterface1 = PatternInterface.Scroll Then  
            Dim owner As ItemsControl = DirectCast(MyBase.Owner, ItemsControl)  
  
            ' ScrollHost is internal to the ItemsControl class  
            If owner.ScrollHost IsNot Nothing Then  
                Dim peer As AutomationPeer = UIElementAutomationPeer.CreatePeerForElement(owner.ScrollHost)  
                If (peer IsNot Nothing) AndAlso (TypeOf peer Is IScrollProvider) Then  
                    peer.EventsSource = Me  
                    Return DirectCast(peer, IScrollProvider)  
                End If  
            End If  
        End If  
        Return MyBase.GetPattern(patternInterface1)  
    End Function  
End Class  

Para especificar um subelemento para manipulação de padrões, esse código obtém o objeto subelement, cria um par usando o método, define a EventsSource propriedade do novo par para o par atual e retorna o CreatePeerForElement novo par. A configuração EventsSource em um subelemento impede que o subelemento apareça na árvore de pares de automação e designa todos os eventos gerados pelo subelemento como originários do controle especificado em EventsSource. O ScrollViewer controle não aparece na árvore de automação e os eventos de rolagem que ele gera parecem se originar do ItemsControl objeto.

Substituir métodos "Core"

O código de automação obtém informações sobre o controle chamando métodos públicos da classe par. Para fornecer informações sobre o controle, substitua cada método cujo nome termina com "Core" quando sua implementação de controle é diferente da que é fornecida pela classe base par de automação. No mínimo, seu controle deve implementar os GetClassNameCore métodos e GetAutomationControlTypeCore , conforme mostrado no exemplo a seguir.

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

protected override AutomationControlType GetAutomationControlTypeCore()
{
    return AutomationControlType.Spinner;
}
Protected Overrides Function GetClassNameCore() As String
    Return "NumericUpDown"
End Function

Protected Overrides Function GetAutomationControlTypeCore() As AutomationControlType
    Return AutomationControlType.Spinner
End Function

Sua implementação de GetAutomationControlTypeCore descreve seu controle retornando um ControlType valor. Embora você possa retornar , você deve retornar ControlType.Customum dos tipos de controle mais específicos se ele descreve com precisão seu controle. Um valor de retorno de requer trabalho extra para o provedor implementar a Automação da Interface do Usuário e os produtos cliente da Automação da Interface do Usuário não conseguem antecipar a estrutura de controle, a interação com o teclado e os possíveis padrões de ControlType.Custom controle.

Implemente os IsContentElementCore métodos e IsControlElementCore para indicar se seu controle contém conteúdo de dados ou cumpre uma função interativa na interface do usuário (ou ambos). Por padrão, os dois os métodos retornam true. Essas configurações aprimoram a usabilidade de ferramentas de automação, como leitores de tela, que podem usar estes métodos para filtrar a árvore de automação. Se o método transferir a manipulação de padrões para um par de subelemento, o método do par de IsControlElementCore subelemento poderá retornar false para ocultar o GetPattern par de subelemento da árvore de automação. Por exemplo, a rolagem em um é manipulada por um ListBoxScrollViewer, e o par de automação para PatternInterface.Scroll é retornado pelo GetPattern método do ScrollViewerAutomationPeer que está associado ao ListBoxAutomationPeer. Portanto, o IsControlElementCoreScrollViewerAutomationPeer método dos retornos false, para que o ScrollViewerAutomationPeer não apareça na árvore de automação.

Seu par de automação deve fornecer valores padrão apropriados para o seu controle. Observe que o XAML que faz referência ao seu controle pode substituir suas implementações de pares de métodos principais incluindo AutomationProperties atributos. Por exemplo, o XAML a seguir cria um botão que tem duas propriedades personalizadas de Automação da Interface do Usuário.

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

Implementar provedores de padrão

As interfaces implementadas por um provedor personalizado são explicitamente declaradas se o elemento proprietário derivar diretamente do Control. Por exemplo, o código a seguir declara um par para um que implementa um Control valor de intervalo.

public class RangePeer1 : FrameworkElementAutomationPeer, IRangeValueProvider { }  
Public Class RangePeer1  
    Inherits FrameworkElementAutomationPeer  
    Implements IRangeValueProvider  
End Class  

Se o controle proprietário deriva de um tipo específico de controle, como RangeBase, o peer pode ser derivado de uma classe de peer derivada equivalente. Nesse caso, o par derivaria de , que fornece uma implementação de base de RangeBaseAutomationPeerIRangeValueProvider. O código a seguir mostra a declaração desse tipo de par.

public class RangePeer2 : RangeBaseAutomationPeer { }  
Public Class RangePeer2  
    Inherits RangeBaseAutomationPeer  
End Class  

Para obter um exemplo de implementação, consulte o código-fonte C# ou Visual Basic que implementa e consome um controle personalizado NumericUpDown.

Acionar eventos

Os clientes de automação podem assinar eventos de automação. Os controles personalizados devem relatar alterações no estado do controle chamando o RaiseAutomationEvent método. Da mesma forma, quando um valor de propriedade for alterado, chame o RaisePropertyChangedEvent método. O código a seguir mostra como obter o objeto par de dentro do código do controle e como chamar um método para acionar um evento. Como uma otimização, o código determina se há algum ouvinte para esse tipo de evento. O acionamento do evento somente quando há ouvintes evita a sobrecarga desnecessária e ajuda o controle a permanecer responsivo.

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

    if (peer != null)
    {
        peer.RaisePropertyChangedEvent(
            RangeValuePatternIdentifiers.ValueProperty,
            (double)oldValue,
            (double)newValue);
    }
}
If AutomationPeer.ListenerExists(AutomationEvents.PropertyChanged) Then
    Dim peer As NumericUpDownAutomationPeer = TryCast(UIElementAutomationPeer.FromElement(nudCtrl), NumericUpDownAutomationPeer)

    If peer IsNot Nothing Then
        peer.RaisePropertyChangedEvent(RangeValuePatternIdentifiers.ValueProperty, CDbl(oldValue), CDbl(newValue))
    End If
End If

Confira também