Pares de automação personalizados

Descreve o conceito de pares de automação para Automação da Interface do Usuário da Microsoft e como você pode fornecer suporte de automação para sua própria classe de interface do usuário personalizada.

Automação da Interface do Usuário fornece uma estrutura que os clientes de automação podem usar para examinar ou operar as interfaces do usuário de uma variedade de plataformas e estruturas de interface do usuário. Se você estiver escrevendo um aplicativo do Windows, as classes que você usa para sua interface do usuário já fornecem suporte a Automação da Interface do Usuário. Você pode derivar de classes existentes e não lacradas para definir um novo tipo de controle de interface do usuário ou classe de suporte. No processo de fazer isso, sua classe pode adicionar um comportamento que deve ter suporte à acessibilidade, mas que o suporte padrão de automação da interface do usuário não cobre. Nesse caso, você deve estender o suporte existente à Automação da Interface do Usuário derivando da classe AutomationPeer que a implementação base usou, adicionando qualquer suporte necessário à implementação do par e informando à infraestrutura de controle de aplicativo do Windows que ela deve criar seu novo par.

Automação da Interface do Usuário permite não apenas aplicativos de acessibilidade e tecnologias adaptativas, como leitores de tela, mas também código de garantia de qualidade (teste). Em qualquer cenário, os clientes de automação da interface do usuário podem examinar elementos da interface do usuário e simular a interação do usuário com seu aplicativo de outro código fora do aplicativo. Para obter informações sobre Automação da Interface do Usuário em todas as plataformas e em seu significado mais amplo, consulte Visão geral da Automação da Interface do Usuário.

Há dois públicos distintos que usam a estrutura de automação da interface do usuário.

  • Os clientes de Automação da Interface do Usuário chamam APIs de Automação da Interface do Usuário para saber mais sobre toda a interface do usuário que é exibida no momento para o usuário. Por exemplo, uma tecnologia adaptativa, como um leitor de tela, atua como um cliente de automação da interface do usuário. A interface do usuário é apresentada como uma árvore de elementos de automação relacionados. O cliente de automação da interface do usuário pode estar interessado em apenas um aplicativo por vez ou em toda a árvore. O cliente de Automação da Interface do Usuário pode usar APIs de Automação da Interface do Usuário para navegar na árvore e ler ou alterar informações nos elementos de automação.
  • Os provedores de Automação da Interface do Usuário contribuem com informações para a árvore de Automação da Interface do Usuário, implementando APIs que expõem os elementos na interface do usuário que eles introduziram como parte de seu aplicativo. Ao criar um novo controle, agora você deve atuar como participante no cenário do provedor de automação da interface do usuário. Como provedor, você deve garantir que todos os clientes de Automação da Interface do Usuário possam usar a estrutura de Automação da Interface do Usuário para interagir com seu controle para fins de acessibilidade e teste.

Normalmente, há APIs paralelas na estrutura de automação da interface do usuário: uma API para clientes de automação da interface do usuário e outra API com nome semelhante para provedores de automação da interface do usuário. Na maior parte, este tópico aborda as APIs para o provedor de automação da interface do usuário e, especificamente, as classes e interfaces que permitem a extensibilidade do provedor nessa estrutura de interface do usuário. Ocasionalmente, mencionamos APIs de automação da interface do usuário que os clientes de automação da interface do usuário usam, para fornecer alguma perspectiva ou fornecer uma tabela de pesquisa que correlaciona as APIs do cliente e do provedor. Para obter mais informações sobre a perspectiva do cliente, consulte Guia do Programador do Cliente de Automação da Interface do Usuário.

Observação

Os clientes de automação da interface do usuário normalmente não usam código gerenciado e normalmente não são implementados como um aplicativo UWP (geralmente são aplicativos da área de trabalho). Automação da Interface do Usuário é baseado em um padrão e não em uma implementação ou estrutura específica. Muitos clientes existentes de Automação da Interface do Usuário, incluindo produtos de tecnologia assistencial, como leitores de tela, usam interfaces COM (Modelo de Objeto de Componente) para interagir com Automação da Interface do Usuário, o sistema e os aplicativos executados em janelas filho. Para obter mais informações sobre as interfaces COM e como escrever um cliente de automação da interface do usuário usando COM, consulte Conceitos básicos de automação da interface do usuário.

Determinando o estado existente do suporte de automação da interface do usuário para sua classe de interface do usuário personalizada

Antes de tentar implementar um par de automação para um controle personalizado, você deve testar se a classe base e seu par de automação já fornecem a acessibilidade ou o suporte de automação de que você precisa. Em muitos casos, a combinação das implementações FrameworkElementAutomationPeer, pares específicos e os padrões que eles implementam pode fornecer uma experiência de acessibilidade básica, mas satisfatória. Se isso é verdade depende de quantas alterações você fez na exposição do modelo de objeto ao controle em comparação com sua classe base. Além disso, isso depende se suas adições à funcionalidade de classe base se correlacionam com novos elementos de interface do usuário no contrato de modelo ou com a aparência visual do controle. Em alguns casos, suas alterações podem introduzir novos aspectos da experiência do usuário que exigem suporte adicional de acessibilidade.

Mesmo que o uso da classe par base existente forneça o suporte básico de acessibilidade, ainda é uma prática recomendada definir um par para que você possa relatar informações precisas de ClassName para Automação da Interface do Usuário para cenários de teste automatizados. Essa consideração é especialmente importante se você estiver escrevendo um controle destinado ao consumo de terceiros.

Classes de pares de automação

A UWP se baseia em técnicas e convenções de automação da interface do usuário existentes usadas por estruturas de interface do usuário de código gerenciado anteriores, como Windows Forms, Windows Presentation Foundation (WPF) e Microsoft Silverlight. Muitas das classes de controle e sua função e finalidade também têm sua origem em uma estrutura de interface do usuário anterior.

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 par para a classe de controle Button.

Observação

Para os fins deste tópico, tratamos as propriedades relacionadas à acessibilidade como sendo mais importantes quando você implementa um par de controle. Mas para um conceito mais geral de suporte a Automação da Interface do Usuário, você deve implementar um par de acordo com as recomendações, conforme documentado pelo Guia do Programador do Provedor de Automação da Interface do Usuário e Conceitos Básicos de Automação da Interface do Usuário. Esses tópicos não abordam as APIs AutomationPeer específicas que você usaria para fornecer as informações na estrutura UWP para Automação da Interface do Usuário, mas descrevem as propriedades que identificam sua classe ou fornecem outras informações ou interação.

Pares, padrões e tipos de controle

Um padrão de controle é uma implementação de interface que expõe um aspecto específico da funcionalidade de um controle para um cliente de automação da interface do usuário. Os clientes de automação da interface do usuário usam as propriedades e os métodos expostos por meio de um padrão de controle para recuperar informações sobre os recursos do controle ou para manipular o comportamento do controle em tempo de execução.

Os padrões de controle permitem categorizar e expor a funcionalidade de um controle independente do tipo ou da aparência do controle. Por exemplo, um controle que apresenta uma interface tabular usa o padrão de controle Grid para expor o número de linhas e colunas na tabela e para permitir que um cliente de automação da interface do usuário recupere itens da tabela. Como outros exemplos, o cliente Automação da Interface do Usuário pode usar o padrão de controle Invoke para controles que podem ser invocados, como botões, e o padrão de controle Scroll para controles que têm barras de rolagem, como caixas de listagem, exibições de lista ou caixas de combinação. Cada padrão de controle representa um tipo separado de funcionalidade e os padrões de controle podem ser combinados para descrever o conjunto completo de funcionalidades com suporte de um controle específico.

Os padrões de controle estão relacionados à interface do usuário como as interfaces se relacionam com objetos COM. No COM, você pode consultar um objeto para saber a quais interfaces ele dá suporte e usar essas interfaces para acessar a funcionalidade. Em Automação da Interface do Usuário, os clientes de Automação da Interface do Usuário podem consultar um elemento de Automação da Interface do Usuário para descobrir quais padrões de controle ele dá suporte e, em seguida, interagir com o elemento e seu controle emparelhado por meio das propriedades, métodos, eventos e estruturas expostas pelos padrões de controle com suporte.

Uma das principais finalidades de um par de automação é relatar a um cliente de automação da interface do usuário quais padrões de controle o elemento de interface do usuário pode dar suporte por meio de seu par. Para fazer isso, os provedores de automação da interface do usuário implementam novos pares que alteram o comportamento do método GetPattern substituindo o método GetPatternCore. Os clientes de Automação da Interface do Usuário fazem chamadas que o provedor de Automação da Interface do Usuário mapeia para chamar GetPattern. Automação da Interface do Usuário os clientes consultam cada padrão específico com o qual desejam interagir. Se o par der suporte ao padrão, ele retornará uma referência de objeto para si mesmo; caso contrário, ele retornará nulo. Se o retorno não for nulo, o cliente Automação da Interface do Usuário espera que ele possa chamar APIs da interface padrão como um cliente, a fim de interagir com esse padrão de controle.

Um tipo de controle é uma maneira de definir amplamente a funcionalidade de um controle que o par representa. Esse é um conceito diferente de um padrão de controle porque, embora um padrão informe à Automação da Interface do Usuário quais informações ele pode obter ou quais ações ele pode executar por meio de uma interface específica, o tipo de controle existe um nível acima disso. Cada tipo de controle tem diretrizes sobre esses aspectos de Automação da Interface do Usuário:

  • Padrões de controle de automação da interface do usuário: um tipo de controle pode dar suporte a mais de um padrão, cada um dos quais representa uma classificação diferente de informações ou interação. Cada tipo de controle tem um conjunto de padrões de controle aos quais o controle deve dar suporte, um conjunto opcional e um conjunto ao qual o controle não deve dar suporte.
  • Valores de propriedade de Automação da Interface do Usuário: cada tipo de controle tem um conjunto de propriedades às quais o controle deve dar suporte. Essas são as propriedades gerais, conforme descrito em Visão geral das propriedades de automação da interface do usuário, não aquelas que são específicas do padrão.
  • Eventos de automação da interface do usuário: cada tipo de controle tem um conjunto de eventos aos quais o controle deve dar suporte. Novamente, eles são gerais, não específicos do padrão, conforme descrito em Visão geral de eventos de automação da interface do usuário.
  • Estrutura de árvore de Automação da Interface do Usuário: cada tipo de controle define como o controle deve aparecer na estrutura de árvore de Automação da Interface do Usuário.

Independentemente de como os pares de automação para a estrutura são implementados, a funcionalidade do cliente de Automação da Interface do Usuário não está vinculada à UWP e, na verdade, é provável que os clientes de Automação da Interface do Usuário existentes, como tecnologias adaptativas, usem outros modelos de programação, como COM. No COM, os clientes podem QueryInterface para a interface de padrão de controle COM que implementa o padrão solicitado ou a estrutura geral de Automação da Interface do Usuário para propriedades, eventos ou exame de árvore. Para os padrões, a estrutura de Automação da Interface do Usuário empacota esse código de interface em execução no código UWP em execução no provedor de Automação da Interface do Usuário do aplicativo e no par relevante.

Ao implementar padrões de controle para uma estrutura de código gerenciado, como um aplicativo UWP usando C# ou Microsoft Visual Basic, você pode usar interfaces do .NET Framework para representar esses padrões em vez de usar a representação da interface COM. Por exemplo, a interface do padrão de automação da interface do usuário para uma implementação do provedor Microsoft .NET do padrão Invoke é IInvokeProvider.

Para obter uma lista de padrões de controle, interfaces de provedor e sua finalidade, consulte Padrões e interfaces de controle. Para obter a lista dos tipos de controle, consulte Visão geral dos tipos de controle de automação da interface do usuário.

Diretrizes sobre como implementar padrões de controle

Os padrões de controle e o que eles pretendem fazem parte de uma definição maior da estrutura de Automação da Interface do Usuário e não se aplicam apenas ao suporte de acessibilidade para um aplicativo UWP. Ao implementar um padrão de controle, você deve certificar-se de implementá-lo de uma maneira que corresponda às diretrizes, conforme documentado nesses documentos e também na especificação de automação da interface do usuário. Se você estiver procurando diretrizes, geralmente poderá usar a documentação da Microsoft e não precisará consultar a especificação. As diretrizes para cada padrão estão documentadas aqui: Implementando padrões de controle de automação da interface do usuário. Você notará que cada tópico nesta área tem uma seção "Diretrizes e Convenções de Implementação" e uma seção "Membros Obrigatórios". As diretrizes geralmente se referem a APIs específicas da interface de padrão de controle relevante na referência de Interfaces de Padrão de Controle para Provedores . Essas interfaces são as interfaces nativas/COM (e suas APIs usam sintaxe no estilo COM). Mas tudo o que você vê lá tem um equivalente no namespace Windows.UI.Xaml.Automation.Provider.

Se você estiver usando os pares de automação padrão e expandindo seu comportamento, esses pares já foram escritos em conformidade com as diretrizes de automação da interface do usuário. Se eles derem suporte a padrões de controle, você poderá contar com esse suporte de padrão em conformidade com as diretrizes em Implementando padrões de controle de automação da interface do usuário. Se um par de controle relatar que é representativo de um tipo de controle definido por Automação da Interface do Usuário, as diretrizes documentadas em Suporte a tipos de controle de automação da interface do usuário foram seguidas por esse par.

No entanto, talvez você precise de diretrizes adicionais para padrões de controle ou tipos de controle para seguir as recomendações de automação da interface do usuário em sua implementação de pares. Isso seria particularmente verdadeiro se você estiver implementando suporte a padrão ou tipo de controle que ainda não existe como uma implementação padrão em um controle UWP. Por exemplo, o padrão para anotações não é implementado em nenhum dos controles XAML padrão. Mas você pode ter um aplicativo que usa anotações extensivamente e, portanto, deseja exibir essa funcionalidade para ser acessível. Para esse cenário, seu par deve implementar IAnnotationProvider e provavelmente deve relatar a si mesmo como o tipo de controle Document com propriedades apropriadas para indicar que seus documentos dão suporte à anotação.

Recomendamos que você use as diretrizes que você vê para os padrões em Implementando padrões de controle de automação da interface do usuário ou tipos de controle em Suporte a tipos de controle de automação da interface do usuário como orientação e diretrizes gerais. Você pode até tentar seguir alguns dos links da API para obter descrições e comentários sobre a finalidade das APIs. Mas para obter as especificações de sintaxe necessárias para a programação de aplicativos UWP, localize a API equivalente no namespace Windows.UI.Xaml.Automation.Provider e use essas páginas de referência para obter mais informações.

Classes de pares de automação integradas

Em geral, os elementos implementam uma classe par de automação se aceitarem a atividade da interface do usuário do usuário ou se contiverem informações necessárias para os usuários de tecnologias adaptativas que representam a interface do usuário interativa ou significativa dos aplicativos. Nem todos os elementos visuais UWP têm pares de automação. Exemplos de classes que implementam pares de automação são Button e TextBox. Exemplos de classes que não implementam pares de automação são Border e classes baseadas em Panel, como Grid e Canvas. Um painel não tem par porque está fornecendo um comportamento de layout que é apenas visual. Não há uma maneira relevante de acessibilidade para o usuário interagir com um painel. Quaisquer elementos filho que um Painel contenha são relatados às árvores de Automação da Interface do Usuário como elementos filho do próximo pai disponível na árvore que tem uma representação de par ou elemento.

Automação da Interface do Usuário e limites de processo UWP

Normalmente, o código do cliente de Automação da Interface do Usuário que acessa um aplicativo UWP é executado fora do processo. A infraestrutura da estrutura de automação da interface do usuário permite que as informações ultrapassem o limite do processo. Esse conceito é explicado com mais detalhes em Conceitos básicos de automação da interface do usuário.

OnCreateAutomationPeer

Todas as classes derivadas de UIElement contêm o método virtual protegido OnCreateAutomationPeer. A sequência de inicialização do objeto para pares de automação chama OnCreateAutomationPeer para obter o objeto par de automação para cada controle e, portanto, construir uma árvore de automação da interface do usuário para uso em tempo de execução. O código de Automação da Interface do Usuário pode usar o par para obter informações sobre as características e os recursos de um controle e para simular o uso interativo por meio de seus padrões de controle. Um controle personalizado que dá suporte à automação deve substituir OnCreateAutomationPeer e retornar uma instância de uma classe derivada de AutomationPeer. Por exemplo, se um controle personalizado derivar da classe ButtonBase, o objeto retornado por OnCreateAutomationPeer deverá derivar de ButtonBaseAutomationPeer.

Se você estiver escrevendo uma classe de controle personalizada e também pretende fornecer um novo par de automação, substitua o método OnCreateAutomationPeer para seu controle personalizado para que ele retorne uma nova instância do seu par. Sua classe par deve derivar direta ou indiretamente de AutomationPeer.

Por exemplo, o código a seguir declara que o controle NumericUpDown personalizado deve usar o par NumericUpDownPeer para fins de automação da interface do usuário.

using Windows.UI.Xaml.Automation.Peers;
...
public class NumericUpDown : RangeBase {
    public NumericUpDown() {
    // other initialization; DefaultStyleKey etc.
    }
    ...
    protected override AutomationPeer OnCreateAutomationPeer()
    {
        return new NumericUpDownAutomationPeer(this);
    }
}
Public Class NumericUpDown
    Inherits RangeBase
    ' other initialization; DefaultStyleKey etc.
       Public Sub New()
       End Sub
       Protected Overrides Function OnCreateAutomationPeer() As AutomationPeer
              Return New NumericUpDownAutomationPeer(Me)
       End Function
End Class
// NumericUpDown.idl
namespace MyNamespace
{
    runtimeclass NumericUpDown : Windows.UI.Xaml.Controls.Primitives.RangeBase
    {
        NumericUpDown();
        Int32 MyProperty;
    }
}

// NumericUpDown.h
...
struct NumericUpDown : NumericUpDownT<NumericUpDown>
{
	...
    Windows::UI::Xaml::Automation::Peers::AutomationPeer OnCreateAutomationPeer()
    {
        return winrt::make<MyNamespace::implementation::NumericUpDownAutomationPeer>(*this);
    }
};
//.h
public ref class NumericUpDown sealed : Windows::UI::Xaml::Controls::Primitives::RangeBase
{
// other initialization not shown
protected:
    virtual AutomationPeer^ OnCreateAutomationPeer() override
    {
         return ref new NumericUpDownAutomationPeer(this);
    }
};

Observação

A implementação OnCreateAutomationPeer não deve fazer nada mais do que inicializar uma nova instância do seu par de automação personalizado, passando o controle de chamada como proprietário e retornar essa instância. Não tente lógica adicional neste método. Em particular, qualquer lógica que possa levar à destruição do AutomationPeer na mesma chamada pode resultar em um comportamento inesperado de runtime.

Em implementações típicas de OnCreateAutomationPeer, o proprietário é especificado como this ou Me porque a substituição do método está no mesmo escopo que o restante da definição da classe de controle.

A definição real da classe de par pode ser feita no mesmo arquivo de código que o controle ou em um arquivo de código separado. Todas as definições de pares existem no namespace Windows.UI.Xaml.Automation.Peers , que é um namespace separado dos controles para os quais eles fornecem pares. Você também pode optar por declarar seus pares em namespaces separados, desde que faça referência aos namespaces necessários para a chamada de método OnCreateAutomationPeer.

Escolhendo a classe base de peer correta

Verifique se o AutomationPeer é derivado de uma classe base que fornece a melhor correspondência para a lógica de par existente da classe de controle da qual você está derivando. No caso do exemplo anterior, porque NumericUpDown deriva de RangeBase, há uma classe RangeBaseAutomationPeer disponível na qual você deve basear seu par. Usando a classe par correspondente mais próxima em paralelo à forma como você deriva o controle em si, você pode evitar a substituição de pelo menos parte da funcionalidade IRangeValueProvider porque a classe par base já a implementa.

A classe Control base não tem uma classe de peer correspondente. Se você precisar que uma classe par corresponda a um controle personalizado derivado de Control, derive a classe par personalizada de FrameworkElementAutomationPeer.

Se você derivar de ContentControl diretamente, essa classe não terá nenhum comportamento de par de automação padrão porque não há nenhuma implementação OnCreateAutomationPeer que faça referência a uma classe par. Portanto, certifique-se de implementar OnCreateAutomationPeer para usar seu próprio par ou usar FrameworkElementAutomationPeer como o par se esse nível de suporte de acessibilidade for adequado para seu controle.

Observação

Normalmente, você não deriva de AutomationPeer em vez de FrameworkElementAutomationPeer. Se você derivou diretamente de AutomationPeer , precisará duplicar muito do suporte básico de acessibilidade que, de outra forma, viria de FrameworkElementAutomationPeer.

Inicialização de uma classe de peer personalizada

O par de automação deve definir um construtor de tipo seguro que usa uma instância do controle proprietário para inicialização base. No próximo exemplo, a implementação passa o valor owner para a base RangeBaseAutomationPeer e, por fim, é o FrameworkElementAutomationPeer que realmente usa owner para definir FrameworkElementAutomationPeer.Owner.

public NumericUpDownAutomationPeer(NumericUpDown owner): base(owner)
{}
Public Sub New(owner As NumericUpDown)
    MyBase.New(owner)
End Sub
// NumericUpDownAutomationPeer.idl
import "NumericUpDown.idl";
namespace MyNamespace
{
    runtimeclass NumericUpDownAutomationPeer : Windows.UI.Xaml.Automation.Peers.AutomationPeer
    {
        NumericUpDownAutomationPeer(NumericUpDown owner);
        Int32 MyProperty;
    }
}

// NumericUpDownAutomationPeer.h
...
struct NumericUpDownAutomationPeer : NumericUpDownAutomationPeerT<NumericUpDownAutomationPeer>
{
    ...
    NumericUpDownAutomationPeer(MyNamespace::NumericUpDown const& owner);
};
//.h
public ref class NumericUpDownAutomationPeer sealed :  Windows::UI::Xaml::Automation::Peers::RangeBaseAutomationPeer
//.cpp
public:    NumericUpDownAutomationPeer(NumericUpDown^ owner);

Métodos principais do AutomationPeer

Por motivos de infraestrutura UWP, os métodos substituíveis de um par de automação fazem parte de um par de métodos: o método de acesso público que o provedor de automação da interface do usuário usa como um ponto de encaminhamento para clientes de automação da interface do usuário e o método de personalização "Core" protegido que uma classe UWP pode substituir para influenciar o comportamento. O par de métodos é conectado por padrão de forma que a chamada para o método de acesso sempre invoque o método "Core" paralelo que tem a implementação do provedor ou, como fallback, invoque uma implementação padrão das classes base.

Ao implementar um par para um controle personalizado, substitua qualquer um dos métodos "Core" da classe de par de automação base em que você deseja expor o comportamento exclusivo do controle personalizado. O código de Automação da Interface do Usuário obtém informações sobre seu controle chamando métodos públicos da classe par. Para fornecer informações sobre o controle, substitua cada método por um nome que termine com "Core" quando a implementação e o design do controle criarem cenários de acessibilidade ou outros cenários de automação da interface do usuário diferentes do que é compatível com a classe par de automação base.

No mínimo, sempre que você definir uma nova classe par, implemente o método GetClassNameCore , conforme mostrado no próximo exemplo.

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

Observação

Talvez você queira armazenar as cadeias de caracteres como constantes em vez de diretamente no corpo do método, mas isso depende de você. Para GetClassNameCore, você não precisará localizar essa cadeia de caracteres. A propriedade LocalizedControlType é usada sempre que uma cadeia de caracteres localizada é necessária para um cliente de automação da interface do usuário, não ClassName.

GetAutomationControlType

Algumas tecnologias adaptativas usam o valor GetAutomationControlType diretamente ao relatar características dos itens em uma árvore de automação da interface do usuário, como informações adicionais além do nome de automação da interface do usuário. Se o controle for significativamente diferente do controle do qual você está derivando e você quiser relatar um tipo de controle diferente do que é relatado pela classe de par base usada pelo controle, você deverá implementar um par e substituir GetAutomationControlTypeCore em sua implementação de par. Isso é particularmente importante se você derivar de uma classe base generalizada, como ItemsControl ou ContentControl, em que o par base não fornece informações precisas sobre o tipo de controle.

Sua implementação de GetAutomationControlTypeCore descreve seu controle retornando um valor AutomationControlType. Embora você possa retornar AutomationControlType.Custom, você deve retornar um dos tipos de controle mais específicos se ele descrever com precisão os principais cenários do controle. Veja um exemplo.

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

Observação

A menos que você especifique AutomationControlType.Custom, não é necessário implementar GetLocalizedControlTypeCore para fornecer um valor de propriedade LocalizedControlType aos clientes. A infraestrutura comum de automação da interface do usuário fornece cadeias de caracteres traduzidas para cada valor AutomationControlType possível diferente de AutomationControlType.Custom.

GetPattern e GetPatternCore

A implementação de GetPatternCore de um par retorna o objeto que dá suporte ao padrão solicitado no parâmetro de entrada. Especificamente, um cliente de Automação da Interface do Usuário chama um método que é encaminhado para o método GetPattern do provedor e especifica um valor de enumeração PatternInterface que nomeia o padrão solicitado. Sua substituição de GetPatternCore deve retornar o objeto que implementa o padrão especificado. Esse objeto é o próprio par, porque o par deve implementar a interface de padrão correspondente sempre que relatar que dá suporte a um padrão. Se o par não tiver uma implementação personalizada de um padrão, mas você souber que a base do par implementa o padrão, poderá chamar a implementação do tipo base de GetPatternCore do GetPatternCore. O GetPatternCore de um par deve retornar null se um padrão não for compatível com o par. No entanto, em vez de retornar null diretamente de sua implementação, você normalmente confiaria na chamada para a implementação base para retornar null para qualquer padrão sem suporte.

Quando há suporte para um padrão, a implementação GetPatternCore pode retornar this ou Me. A expectativa é que o cliente de Automação da Interface do Usuário converta o valor retornado GetPattern para a interface de padrão solicitada sempre que ele não for nulo.

Se uma classe par herdar de outro par e todo o suporte necessário e o relatório de padrões já forem tratados pela classe base, a implementação de GetPatternCore não será necessária. Por exemplo, se você estiver implementando um controle de intervalo derivado de RangeBase e seu par derivar de RangeBaseAutomationPeer, esse par retornará a si mesmo para PatternInterface.RangeValue e terá implementações de trabalho da interface IRangeValueProvider que dá suporte ao padrão.

Embora não seja o código literal, este exemplo se aproxima da implementação de GetPatternCore já presente em RangeBaseAutomationPeer.

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

Se você estiver implementando um par em que não tem todo o suporte necessário de uma classe par base ou quiser alterar ou adicionar ao conjunto de padrões herdados de base que seu par pode dar suporte, substitua GetPatternCore para permitir que os clientes de Automação da Interface do Usuário usem os padrões.

Para obter uma lista dos padrões de provedor disponíveis na implementação UWP do suporte a Automação da Interface do Usuário, consulte Windows.UI.Xaml.Automation.Provider. Cada um desses padrões tem um valor correspondente da enumeração PatternInterface, que é como os clientes de automação da interface do usuário solicitam o padrão em uma chamada GetPattern.

Um par pode relatar que dá suporte a mais de um padrão. Nesse caso, a substituição deve incluir a lógica do caminho de retorno para cada valor PatternInterface com suporte e retornar o par em cada caso correspondente. Espera-se que o chamador solicite apenas uma interface por vez, e cabe ao chamador transmitir para a interface esperada.

Aqui está um exemplo de uma substituição de GetPatternCore para um par personalizado. Ele relata o suporte para dois padrões, IRangeValueProvider e IToggleProvider. O controle aqui é um controle de exibição de mídia que pode ser exibido como tela inteira (o modo de alternância) e que tem uma barra de progresso na qual os usuários podem selecionar uma posição (o controle de intervalo). Esse código veio do exemplo de acessibilidade XAML.

protected override object GetPatternCore(PatternInterface patternInterface)
{
    if (patternInterface == PatternInterface.RangeValue)
    {
        return this;
    }
    else if (patternInterface == PatternInterface.Toggle)
    {
        return this;
    }
    return null;
}

Padrões de encaminhamento de subelementos

Uma implementação do método GetPatternCore também pode especificar um subelemento ou parte como um provedor de padrões para seu host. Este exemplo imita como ItemsControl transfere a manipulação de padrão de rolagem para o par de seu controle ScrollViewer interno. Para especificar um subelemento para manipulação de padrões, esse código obtém o objeto de subelemento, cria um par para o subelemento usando o método FrameworkElementAutomationPeer.CreatePeerForElement e retorna o novo par.

protected override object GetPatternCore(PatternInterface patternInterface)
{
    if (patternInterface == PatternInterface.Scroll)
    {
        ItemsControl owner = (ItemsControl) base.Owner;
        UIElement itemsHost = owner.ItemsHost;
        ScrollViewer element = null;
        while (itemsHost != owner)
        {
            itemsHost = VisualTreeHelper.GetParent(itemsHost) as UIElement;
            element = itemsHost as ScrollViewer;
            if (element != null)
            {
                break;
            }
        }
        if (element != null)
        {
            AutomationPeer peer = FrameworkElementAutomationPeer.CreatePeerForElement(element);
            if ((peer != null) && (peer is IScrollProvider))
            {
                return (IScrollProvider) peer;
            }
        }
    }
    return base.GetPatternCore(patternInterface);
}

Outros métodos principais

Seu controle pode precisar dar suporte a equivalentes de teclado para cenários primários; para obter mais informações sobre por que isso pode ser necessário, consulte Acessibilidade do teclado. A implementação do suporte à chave é necessariamente parte do código de controle e não do código par porque isso faz parte da lógica de um controle, mas sua classe par deve substituir os métodos GetAcceleratorKeyCore e GetAccessKeyCore para relatar aos clientes de Automação da Interface do Usuário quais chaves são usadas. Considere que as cadeias de caracteres que relatam informações importantes podem precisar ser localizadas e, portanto, devem vir de recursos, não de cadeias de caracteres embutidas em código.

Se você estiver fornecendo um par para uma classe que dá suporte a uma coleção, é melhor derivar de classes funcionais e classes pares que já têm esse tipo de suporte de coleção. Se você não puder fazer isso, os pares de controles que mantêm coleções filho podem ter que substituir o método par relacionado à coleção GetChildrenCore para relatar corretamente as relações pai-filho para a árvore de Automação da Interface do Usuário.

Implemente os métodos IsContentElementCore e IsControlElementCore para indicar se o controle contém conteúdo de dados ou cumpre uma função interativa na interface do usuário (ou ambos). Por padrão, ambos os métodos retornam true. Essas configurações melhoram a usabilidade de tecnologias assistivas, como leitores de tela, que podem usar esses métodos para filtrar a árvore de automação. Se o método GetPatternCore transferir a manipulação de padrões para um par de subelemento, o método IsControlElementCore do par de subelemento poderá retornar false para ocultar o par de subelemento da árvore de automação.

Alguns controles podem dar suporte a cenários de rotulagem, em que uma parte de rótulo de texto fornece informações para uma parte que não é de texto ou um controle deve estar em uma relação de rotulagem conhecida com outro controle na interface do usuário. Se for possível fornecer um comportamento útil baseado em classe, você poderá substituir GetLabeledByCore para fornecer esse comportamento.

GetBoundingRectangleCore e GetClickablePointCore são usados principalmente para cenários de teste automatizados. Se você quiser dar suporte a testes automatizados para seu controle, talvez queira substituir esses métodos. Isso pode ser desejado para controles do tipo intervalo, em que você não pode sugerir apenas um único ponto porque onde o usuário clica no espaço de coordenadas tem um efeito diferente em um intervalo. Por exemplo, o par de automação ScrollBar padrão substitui GetClickablePointCore para retornar um valor de ponto "não é um número".

GetLiveSettingCore influencia o padrão de controle para o valor LiveSetting para Automação da Interface do Usuário. Talvez você queira substituir isso se quiser que seu controle retorne um valor diferente de AutomationLiveSetting.Off. Para obter mais informações sobre o que o LiveSetting representa, consulte AutomationProperties.LiveSetting.

Você pode substituir GetOrientationCore se o controle tiver uma propriedade de orientação configurável que possa ser mapeada para AutomationOrientation. As classes ScrollBarAutomationPeer e SliderAutomationPeer fazem isso.

Implementação base em FrameworkElementAutomationPeer

A implementação base de FrameworkElementAutomationPeer fornece algumas informações de Automação da Interface do Usuário que podem ser interpretadas de várias propriedades de layout e comportamento definidas no nível da estrutura.

Observação

Os pares UWP padrão implementam um comportamento usando código nativo interno que implementa a UWP, não necessariamente usando o código UWP real. Você não poderá ver o código ou a lógica da implementação por meio da reflexão CLR (Common Language Runtime) ou de outras técnicas. Você também não verá páginas de referência distintas para substituições específicas de subclasse do comportamento de pares base. Por exemplo, pode haver comportamento adicional para GetNameCore de um TextBoxAutomationPeer, que não será descrito na página de referência AutomationPeer.GetNameCore e não há nenhuma página de referência para TextBoxAutomationPeer.GetNameCore. Não há nem mesmo uma página de referência TextBoxAutomationPeer.GetNameCore . Em vez disso, leia o tópico de referência para a classe de pares mais imediata e procure notas de implementação na seção Comentários.

Pares e AutomationProperties

Seu par de automação deve fornecer valores padrão apropriados para as informações relacionadas à acessibilidade do controle. Observe que qualquer código de aplicativo que usa o controle pode substituir parte desse comportamento incluindo valores de propriedade anexada AutomationProperties em instâncias de controle. Os chamadores podem fazer isso para os controles padrão ou para controles personalizados. 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."/>

Para obter mais informações sobre as propriedades anexadas AutomationProperties, consulte Informações básicas de acessibilidade.

Alguns dos métodos AutomationPeer existem devido ao contrato geral de como os provedores de automação da interface do usuário devem relatar informações, mas esses métodos normalmente não são implementados em pares de controle. Isso ocorre porque espera-se que essas informações sejam fornecidas por valores AutomationProperties aplicados ao código do aplicativo que usa os controles em uma interface do usuário específica. Por exemplo, a maioria dos aplicativos definiria a relação de rotulagem entre dois controles diferentes na interface do usuário aplicando um valor AutomationProperties.LabeledBy. No entanto, LabeledByCore é implementado em determinados pares que representam relações de dados ou itens em um controle, como usar uma parte de cabeçalho para rotular uma parte de campo de dados, rotular itens com seus contêineres ou cenários semelhantes.

Implementando padrões

Vejamos como escrever um par para um controle que implementa um comportamento de expansão-recolhimento implementando a interface de padrão de controle para expandir-recolher. O par deve habilitar a acessibilidade para o comportamento de expansão-recolhimento retornando a si mesmo sempre que GetPattern for chamado com um valor de PatternInterface.ExpandCollapse. Em seguida, o par deve herdar a interface do provedor para esse padrão (IExpandCollapseProvider) e fornecer implementações para cada um dos membros dessa interface do provedor. Nesse caso, a interface tem três membros a serem substituídos: Expand, Collapse, ExpandCollapseState.

É útil planejar com antecedência a acessibilidade no design da API da própria classe. Sempre que você tiver um comportamento potencialmente solicitado como resultado de interações típicas com um usuário que está trabalhando na interface do usuário ou por meio de um padrão de provedor de automação, forneça um único método que a resposta da interface do usuário ou o padrão de automação possa chamar. Por exemplo, se o controle tiver partes de botão que tenham manipuladores de eventos conectados que podem expandir ou recolher o controle e tiver equivalentes de teclado para essas ações, faça com que esses manipuladores de eventos chamem o mesmo método que você chama de dentro do corpo das implementações Expand ou Collapse para IExpandCollapseProvider no par. Usar um método lógico comum também pode ser uma maneira útil de garantir que os estados visuais do controle sejam atualizados para mostrar o estado lógico de maneira uniforme, independentemente de como o comportamento foi invocado.

Uma implementação típica é que as APIs do provedor primeiro chamam o Proprietário para acessar a instância de controle em tempo de execução. Em seguida, os métodos de comportamento necessários podem ser chamados nesse objeto.

public class IndexCardAutomationPeer : FrameworkElementAutomationPeer, IExpandCollapseProvider {
    private IndexCard ownerIndexCard;
    public IndexCardAutomationPeer(IndexCard owner) : base(owner)
    {
         ownerIndexCard = owner;
    }
}

Uma implementação alternativa é que o próprio controle pode fazer referência ao seu par. Esse é um padrão comum se você estiver gerando eventos de automação do controle, pois o método RaiseAutomationEvent é um método par.

Eventos de automação da interface do usuário

Os eventos de Automação da Interface do Usuário estão nas seguintes categorias.

Evento Descrição
Alteração da propriedade É acionado quando uma propriedade em um elemento de automação da interface do usuário ou padrão de controle é alterada. Por exemplo, se um cliente precisar monitorar o controle de caixa de seleção de um aplicativo, ele poderá se registrar para escutar um evento de alteração de propriedade na propriedade ToggleState. Quando o controle da caixa de seleção está marcado ou desmarcado, o provedor aciona o evento e o cliente pode agir conforme necessário.
Ação de elemento Acionado quando uma alteração na interface do usuário resulta de atividade programática ou do usuário; por exemplo, quando um botão é clicado ou chamado por meio do padrão Invoke .
Alteração de estrutura É acionado quando a estrutura da árvore de automação da interface do usuário é alterada. A estrutura muda quando novos itens da interface do usuário se tornam visíveis, ocultos ou são removidos da área de trabalho.
Mudança global É acionado quando ocorrem ações de interesse global para o cliente, como quando o foco muda de um elemento para outro ou quando uma janela filho é fechada. Alguns eventos não indicam necessariamente que o estado da interface do usuário mudou. Por exemplo, se o usuário tabular para um campo de entrada de texto e clicar em um botão para atualizar o campo, um evento TextChanged será acionado mesmo que o usuário não tenha realmente alterado o texto. Ao processar um evento, pode ser necessário que o aplicativo cliente verifique se algo realmente mudou antes de executar uma ação.

Identificadores AutomationEvents

Os eventos de Automação da Interface do Usuário são identificados por valores AutomationEvents . Os valores da enumeração identificam exclusivamente o tipo de evento.

Gerando eventos

Automação da Interface do Usuário os clientes podem assinar eventos de automação. No modelo de par de automação, os pares para controles personalizados devem relatar alterações no estado de controle que são relevantes para a acessibilidade chamando o método RaiseAutomationEvent . Da mesma forma, quando um valor de propriedade chave de Automação da Interface do Usuário é alterado, os pares de controle personalizados devem chamar o método RaisePropertyChangedEvent .

O próximo exemplo de código mostra como obter o objeto par de dentro do código de definição de controle e chamar um método para disparar um evento desse par. Como uma otimização, o código determina se há ouvintes para esse tipo de evento. Disparar o evento e criar o objeto par somente quando há ouvintes evita sobrecarga desnecessária e ajuda o controle a permanecer responsivo.

if (AutomationPeer.ListenerExists(AutomationEvents.PropertyChanged))
{
    NumericUpDownAutomationPeer peer =
        FrameworkElementAutomationPeer.FromElement(nudCtrl) as NumericUpDownAutomationPeer;
    if (peer != null)
    {
        peer.RaisePropertyChangedEvent(
            RangeValuePatternIdentifiers.ValueProperty,
            (double)oldValue,
            (double)newValue);
    }
}

Navegação de pares

Depois de localizar um par de automação, um cliente de automação da interface do usuário pode navegar pela estrutura de pares de um aplicativo chamando os métodos GetChildren e GetParent do objeto par. A navegação entre elementos de interface do usuário dentro de um controle é suportada pela implementação do peer do método GetChildrenCore. O sistema de automação da interface do usuário chama esse método para criar uma árvore de subelementos contidos em um controle; por exemplo, listar itens em uma caixa de listagem. O método GetChildrenCore padrão em FrameworkElementAutomationPeer percorre a árvore visual de elementos para criar a árvore de pares de automação. Os controles personalizados podem substituir esse método para expor uma representação diferente de elementos filho para clientes de automação, retornando os pares de automação de elementos que transmitem informações ou permitem a interação do usuário.

Suporte de automação nativa para padrões de texto

Alguns dos pares de automação de aplicativo UWP padrão fornecem suporte ao padrão de controle para o padrão de texto (PatternInterface.Text). Mas eles fornecem esse suporte por meio de métodos nativos, e os pares envolvidos não observarão a interface ITextProvider na herança (gerenciada). Ainda assim, se um cliente de Automação da Interface do Usuário gerenciado ou não gerenciado consultar o par em busca de padrões, ele relatará suporte para o padrão de texto e fornecerá comportamento para partes do padrão quando as APIs do cliente forem chamadas.

Se você pretende derivar de um dos controles de texto do aplicativo UWP e também criar um par personalizado que deriva de um dos pares relacionados ao texto, verifique as seções Comentários do par para saber mais sobre qualquer suporte de nível nativo para padrões. Você pode acessar o comportamento de base nativa em seu par personalizado se chamar a implementação base de suas implementações de interface de provedor gerenciado, mas é difícil modificar o que a implementação base faz porque as interfaces nativas no par e em seu controle de proprietário não são expostas. Geralmente, você deve usar as implementações base no estado em que se encontram (somente base de chamadas) ou substituir completamente a funcionalidade por seu próprio código gerenciado e não chamar a implementação base. O último é um cenário avançado, você precisará de uma boa familiaridade com a estrutura de serviços de texto que está sendo usada pelo controle para dar suporte aos requisitos de acessibilidade ao usar essa estrutura.

AutomationProperties.AccessibilityView

Além de fornecer um par personalizado, você também pode ajustar a representação de exibição de árvore para qualquer instância de controle, definindo AutomationProperties.AccessibilityView em XAML. Isso não é implementado como parte de uma classe par, mas vamos mencioná-lo aqui porque é pertinente ao suporte geral de acessibilidade para controles personalizados ou para modelos que você personaliza.

O cenário principal para usar AutomationProperties.AccessibilityView é omitir deliberadamente determinados controles em um modelo das exibições de Automação da Interface do Usuário, pois eles não contribuem significativamente para a exibição de acessibilidade de todo o controle. Para evitar isso, defina AutomationProperties.AccessibilityView como "Raw".

Lançando exceções de pares de automação

As APIs que você está implementando para o suporte a pares de automação têm permissão para gerar exceções. Espera-se que todos os clientes de automação da interface do usuário que estão escutando sejam robustos o suficiente para continuar depois que a maioria das exceções for gerada. Com toda a probabilidade, esse ouvinte está olhando para uma árvore de automação completa que inclui aplicativos diferentes dos seus, e é um design de cliente inaceitável derrubar todo o cliente apenas porque uma área da árvore lançou uma exceção baseada em pares quando o cliente chamou suas APIs.

Para parâmetros que são passados para seu par, é aceitável validar a entrada e, por exemplo, lançar ArgumentNullException se ela foi passada nula e esse não é um valor válido para sua implementação. No entanto, se houver operações subsequentes executadas pelo par, lembre-se de que as interações do par com o controle de hospedagem têm algo de caráter assíncrono para elas. Qualquer coisa que um par faça não necessariamente bloqueará o thread da interface do usuário no controle (e provavelmente não deveria). Portanto, você pode ter situações em que um objeto estava disponível ou tinha determinadas propriedades quando o par foi criado ou quando um método par de automação foi chamado pela primeira vez, mas, enquanto isso, o estado de controle foi alterado. Para esses casos, há duas exceções dedicadas que um provedor pode gerar:

  • Lance ElementNotAvailableException se você não conseguir acessar o proprietário do par ou um elemento par relacionado com base nas informações originais que sua API foi passada. Por exemplo, você pode ter um par que está tentando executar seus métodos, mas o proprietário foi removido da interface do usuário, como uma caixa de diálogo modal que foi fechada. Para um cliente non-.NET, isso é mapeado para UIA_E_ELEMENTNOTAVAILABLE.
  • Lance ElementNotEnabledException se ainda houver um proprietário, mas esse proprietário estiver em um modo como IsEnabled=false que está bloqueando algumas das alterações programáticas específicas que seu par está tentando realizar. Para um cliente non-.NET, isso é mapeado para UIA_E_ELEMENTNOTENABLED.

Além disso, os pares devem ser relativamente conservadores em relação às exceções que lançam de seu apoio de pares. A maioria dos clientes não será capaz de lidar com exceções de pares e transformá-las em escolhas acionáveis que seus usuários podem fazer ao interagir com o cliente. Portanto, às vezes, uma não operação e a captura de exceções sem relançar em suas implementações de pares é uma estratégia melhor do que lançar exceções toda vez que algo que o par tenta fazer não funciona. Considere também que a maioria dos clientes de automação da interface do usuário não é escrita em código gerenciado. A maioria é escrita em COM e está apenas verificando se há S_OK em um HRESULT sempre que chama um método de cliente de Automação da Interface do Usuário que acaba acessando seu par.