Compartilhar via


Garantindo que os elementos da interface do usuário sejam nomeados corretamente

Este tópico descreve a maneira correta de especificar os nomes dos elementos da interface do usuário em seus aplicativos Microsoft Win32 para que o Microsoft Active Accessibility possa expor com precisão os nomes para aplicativos cliente por meio da propriedadeIAccessible Name.

As informações nesta seção se aplicam somente ao Microsoft Active Accessibility. Ele não se aplica a aplicativos que usam a Automação da Interface do Usuário Microsoft ou aqueles baseados em linguagens de marcação, como HTML, HTML dinâmico (DHTML) ou XML.

Visão geral

No Microsoft Active Accessibility, cada elemento da interface do usuário em um aplicativo é representado por um objeto que expõe a interface IAccessible. Os aplicativos cliente usam as propriedades e os métodos da interface IAccessible para interagir com o elemento da interface do usuário e recuperar informações sobre ele. Uma das propriedades mais importantes expostas pela interface IAccessible é a propriedade Name. Os aplicativos cliente dependem da propriedade Name para localizar, identificar ou anunciar um elemento de interface do usuário para o usuário. Se o Microsoft Active Accessibility não puder expor corretamente a propriedade Name de um elemento de interface do usuário específico, os aplicativos cliente não poderão apresentar esse elemento de interface do usuário ao usuário e o elemento de interface do usuário ficará inacessível para usuários com deficiências.

Como a nomeação incorreta causa problemas

Para ilustrar os problemas causados pela nomeação incorreta de elementos da interface do usuário, considere o formulário de entrada de nome mostrado na ilustração a seguir.

ilustração de um formulário simples para inserir nome e sobrenome

Embora os elementos da interface do usuário no formulário pareçam ok, a implementação programática está incorreta. Para um cliente do Microsoft Active Accessibility como um leitor de tela, a propriedade Name do controle de edição superior é "Sobrenome:", e a propriedade Name do controle de edição inferior é uma cadeia de caracteres vazia (""). O leitor de tela lerá o controle de edição superior como "Sobrenome", embora se espere que o usuário digite o primeiro nome. O leitor de tela lerá o segundo controle de edição como "sem nome", para que o usuário não tenha ideia do que digitar no segundo controle de edição. O leitor de tela não pode ajudar o usuário a inserir dados neste formulário simples.

Outro problema com o formulário é que nenhuma tecla de atalho é atribuída a nenhum dos controles de edição. O usuário é forçado a usar tabulação para acessar os controles ou usar o mouse.

As seções a seguir explicam a origem desses problemas e fornecem diretrizes para corrigi-los.

Como o MSAA obtém a propriedade Name

O Microsoft Active Accessibility obtém a cadeia de caracteres da propriedade Name de locais diferentes, dependendo do tipo do elemento da interface do usuário. Para a maioria dos elementos da interface do usuário que têm texto de janela associado, o Microsoft Active Accessibility usa o texto da janela como a cadeia de caracteres da propriedade Name. Exemplos desse tipo de elemento de interface do usuário incluem controles como botões, itens de menu e dicas de ferramentas.

Para os controles a seguir, o Microsoft Active Accessibility ignora o texto da janela e, em vez disso, procura um rótulo de texto estático (ou rótulo de caixa de grupo) imediatamente anterior ao controle na ordem de tabulação.

  • Caixas de combinação
  • Seletores de data e hora
  • Controles de edição e de edição avançada
  • Controles de endereço IP
  • Caixas de listagem
  • Exibições de lista
  • Barras de progresso
  • Barras de rolagem
  • Controles estáticos que têm o estilo SS_ICON ou SS_BITMAP
  • Barras de faixas
  • Exibições de árvore

Se os controles anteriores não forem acompanhados por rótulos de texto estáticos ou se os rótulos não forem implementados corretamente, o Microsoft Active Accessibility não poderá fornecer a propriedade Name correta para aplicativos cliente.

A maioria dos controles anteriores, na realidade, tem texto de janela associado. O editor de recursos gera automaticamente o texto da janela, que consiste em uma cadeia de caracteres genérica como "edit1" ou "listbox3". Embora os desenvolvedores possam substituir o texto da janela gerada por um texto mais significativo, a maioria nunca o faz. Como o texto da janela gerado não tem significado para o usuário, o Microsoft Active Accessibility o ignora e usa o rótulo de texto estático que o acompanha.

Como localizar e corrigir problemas de nomenclatura

No formulário de entrada de nome mostrado em Como a nomeação incorreta causa problemas, a causa dos problemas é que a ordem de tabulação dos controles está incorreta. Examinar a interface do usuário com uma ferramenta de teste como Inspect revelaria os problemas com a hierarquia de objetos. A captura de tela a seguir mostra a hierarquia de objetos interrompida do formulário de entrada de nome como ele aparece no Inspect.

captura de tela da ferramenta Inspecionar mostrando uma hierarquia de objetos incorreta do formulário de entrada de nome

Na captura de tela anterior, observe que a hierarquia de objetos não corresponde à estrutura dos controles como eles aparecem na interface do usuário do formulário de entrada de nome. Observe também que o Inspect atribuiu o nome incorreto ao penúltimo item (é o controle de edição para inserir o primeiro nome e deve ser um "Nome:" nomeado. Finalmente, observe que o Inspect não pôde encontrar um nome para o último item (é o controle de edição para inserir o sobrenome e deve ser chamado "Sobrenome:".

O exemplo a seguir mostra o conteúdo do arquivo de recurso para o formulário de entrada de nome. Observe que a ordem de tabulação não é consistente com a estrutura lógica dos controles como eles aparecem na interface do usuário. Observe também que nenhuma tecla de atalho é especificada para os dois controles de edição.

IDD_INPUTNAME DIALOGEX 22, 17, 312, 118
STYLE DS_SETFONT | DS_MODALFRAME | WS_CAPTION | WS_SYSMENU
CAPTION "Enter your name"
FONT 8, "System", 0, 0, 0x0
BEGIN
    DEFPUSHBUTTON   "OK",IDOK,179,35,30,11,WS_GROUP
    LTEXT           "First Name:",IDC_STATIC,8,16,43,8
    LTEXT           "Last Name:",IDC_STATIC,8,33,43,8
    EDITTEXT        IDC_EDIT1,53,15,120,12,ES_AUTOHSCROLL
    EDITTEXT        IDC_EDIT2,53,34,120,12,ES_AUTOHSCROLL
END

Para corrigir os problemas com o formulário de entrada de nome, o arquivo de recurso (.rc) deve ser editado para especificar atalhos de teclado e os controles devem ser colocados na seguinte ordem:

  1. O rótulo de texto estático "&Nome:".
  2. O controle de edição para inserir o nome (IDC_EDIT1).
  3. O rótulo de texto estático "&Sobrenome:".
  4. O controle de edição para inserir o sobrenome (IDC_EDIT2).
  5. O botão de ação padrão "OK".

O seguinte exemplo mostra o arquivo de recurso corrigido para o formulário de entrada de nome:

IDD_INPUTNAME DIALOGEX 22, 17, 312, 118
STYLE DS_SETFONT | DS_MODALFRAME | WS_CAPTION | WS_SYSMENU
CAPTION "Enter your name"
FONT 8, "System", 0, 0, 0x0
BEGIN
    LTEXT           "&First Name:",IDC_STATIC,8,16,43,8
    EDITTEXT        IDC_EDIT1,53,15,120,12,ES_AUTOHSCROLL
    LTEXT           "&Last Name:",IDC_STATIC,8,33,43,8
    EDITTEXT        IDC_EDIT2,53,34,120,12,ES_AUTOHSCROLL
    DEFPUSHBUTTON   "OK",IDOK,179,35,30,11,WS_GROUP
END

Para fazer correções em um arquivo de recurso, você pode editar o arquivo diretamente ou pode usar a ferramenta Ordem de tabulação no Microsoft Visual Studio. Você pode acessar a ferramenta Ordem de tabulação no Visual Studio pressionando CTRL+D ou selecionando Ordem de tabulação no menu Formatar.

Depois de corrigir e recriar o aplicativo, a interface do usuário do formulário de entrada de nomes terá a mesma aparência de antes. No entanto, o Microsoft Active Accessibility agora fornecerá as propriedades Name corretas para aplicativos cliente e definirá o foco corretamente quando o usuário pressionar os atalhos de teclado ALT+F ou ALT+L. Além disso, o Inspect exibirá a hierarquia de objetos correta, como mostrado a captura de tela a seguir.

captura de tela da ferramenta Gerenciador Acessível mostrando uma hierarquia de objeto correta do formulário de entrada de nome

Como nomear corretamente uma barra de faixas

Ao definir uma barra de controle (ou controle deslizante), verifique se o rótulo de texto estático principal da barra de controle aparece antes da barra de controle e se os rótulos de texto estático para os intervalos mínimo e máximo aparecem após a barra de faixas. Lembre-se de que o Microsoft Active Accessibility usa o rótulo de texto estático que precede imediatamente um controle como a propriedade Name para o controle. Colocar o rótulo de texto estático principal imediatamente antes da barra de controle e os outros rótulos depois dela garante que o Microsoft Active Accessibility forneça a propriedade Name correta para um cliente.

A ilustração a seguir mostra uma barra de faixas típica com um rótulo de texto estático principal chamado "Velocidade" e rótulos de texto estático para os intervalos mínimo ("mín") e máximo ("máx").

Ilustração de um controle de barra de controle que tem um rótulo principal e rótulos para os intervalos mínimo e máximo

O seguinte exemplo mostra a maneira correta de definir uma barra de controle e seus rótulos de texto estático no arquivo de recurso:

BEGIN
    ...

    LTEXT           "&Speed",IDC_STATIC,47,20,43,8
    CONTROL         "",IDC_SLIDER1,"msctls_trackbar32",
                    TBS_AUTOTICKS | TBS_BOTH | WS_TABSTOP,
                    32,32,62,23
    LTEXT           "min",IDC_STATIC,16,37,15,8
    LTEXT           "max",IDC_STATIC,94,38,43,8

    ...
END

Como usar rótulos invisíveis para nomear controles

Nem sempre é possível ou desejável ter um rótulo visível para cada controle. Por exemplo, às vezes, adicionar rótulos pode causar alterações indesejáveis na aparência da interface do usuário. Nesse caso, você pode usar rótulos invisíveis. O Microsoft Active Accessibility ainda selecionará o texto associado a um rótulo invisível, mas o rótulo não aparecerá nem interferirá na interface do usuário do visual.

Assim como acontece com rótulos visíveis, um rótulo invisível precisa preceder imediatamente o controle na ordem de tabulação. Para tornar um rótulo invisível em um arquivo de recurso (.rc), adicione NOT WS_VISIBLE ou |~WS_VISIBLE à parte de estilo do controle de texto estático. Se você estiver usando o Editor de recursos no Visual Studio, poderá definir a propriedade Visible como False.

Como usar a anotação direta para especificar a propriedade Name

Os proxies padrão incluídos no componente de tempo de execução do Microsoft Active Accessibility, Oleacc.dll, fornecem automaticamente um objeto IAccessible para todos os controles padrão do Windows. Se você personalizar um controle padrão do Windows, os proxies padrão farão o possível para fornecer com precisão todas as propriedades IAccessible para seu controle personalizado. Você deve testar completamente um controle personalizado para garantir que os proxies padrão estejam fornecendo valores de propriedade precisos e completos. Se o teste revelar valores de propriedade imprecisos ou incompletos, você poderá usar a técnica de Anotação Dinâmica chamada anotação direta para fornecer valores de propriedade corretos e adicionar aqueles que estão faltando.

Observe que a Anotação Dinâmica não é apenas para controles compatíveis com os proxies do Microsoft Active Accessibility. Você também pode usá-la para modificar ou fornecer propriedades para qualquer controle que forneça uma implementação própria de IAccessible.

Esta seção se concentra no uso da anotação direta para fornecer um valor correto para a propriedade Name do objeto IAccessible para um controle. Você também pode usar a anotação direta para fornecer outros valores de propriedades. Além disso, outras técnicas de Anotação Dinâmica além da anotação direta estão disponíveis, e os recursos e capacidades da API de Anotação Dinâmica vão muito além do que é descrito nesta seção. Para obter mais informações sobre anotação dinâmica, consulte API de anotação dinâmica.

Etapas para anotar a propriedade Name

O uso da anotação direta para alterar a propriedade Name de um controle envolve as etapas a seguir.

  1. Incluir os seguintes arquivos de cabeçalho:

    • Initguid.h
    • Oleacc.h

    Observação

    Para definir GUIDs, você precisa incluir Initguid.h antes de Oleacc.h no mesmo arquivo.

     

  2. Inicialize a biblioteca COM (Component Object Model) chamando a função CoInitializeEx, normalmente durante o processo de inicialização do aplicativo.

  3. Logo após o controle de destino ser criado (normalmente durante a mensagem WM_INITDIALOG), crie uma instância do gerenciador de anotações e obtenha um ponteiro para seu ponteiro IAccPropServices.

  4. Anote a propriedade Name do controle de destino usando o método IAccPropServices::SetHwndPropStr.

  5. Solte o ponteiro IAccPropServices.

  6. Antes que o controle de destino seja destruído (normalmente ao manipular a mensagem WM_DESTROY), crie uma instância do gerenciador de anotações e obtenha um ponteiro para a Interface IAccPropServices correspondente.

  7. Use o método IAccPropServices::ClearHwndProps para limpar as anotações da propriedade Name do controle de destino.

  8. Solte o ponteiro IAccPropServices.

  9. Antes que o aplicativo saia (normalmente durante o processamento da mensagem WM_DESTROY), solte a biblioteca COM chamando a função CoUninitialize.

A função IAccPropServices::SetHwndPropStr usa cinco parâmetros. Os três primeiros, hwnd, idObject e idChild, combinam-se para identificar o controle. O quarto parâmetro, idProp, especifica o identificador da propriedade a ser alterada. Para alterar a propriedade Name, defina idProp como PROPID_ACC_NAME. (Para obter uma lista de outras propriedades que você pode definir por meio de anotação direta, consulte Como usar a anotação direta.) O último parâmetro de SetHwndPropStr, str, é a nova cadeia de caracteres a ser usada como a propriedade Name.

Exemplo de como anotar a propriedade Name

O código de exemplo a seguir mostra como usar a anotação direta para alterar a propriedade Name do objeto IAccessible para um controle. Para manter as coisas simples, o exemplo usa uma cadeia de caracteres embutida em código ("New Control Name") para definir a propriedade Name. Cadeias de caracteres embutidas em código não devem ser usadas na versão final do seu aplicativo porque não podem ser localizadas. Em vez disso, sempre carregue cadeias de caracteres do arquivo de recurso. Além disso, o exemplo não mostra as chamadas para as funções CoInitializeEx e CoUninitialize.

#include <initguid.h>
#include <oleacc.h>

// AnnotateControlName - Uses direct annotation to change the Name property 
// of the IAccessible object for a control.
//
// hDlg - Handle of the dialog box that contains the control.
// hwndCtl - Handle of the control whose Name property is to be changed.
HRESULT AnnotateControlName(HWND hDlg, HWND hwndCtl)
{
    HRESULT hr;        

    IAccPropServices *pAccPropSvc = NULL;  

    // Create an instance of the annotation manager and retrieve the 
    // IAccPropServices pointer.
    hr = CoCreateInstance(CLSID_AccPropServices, NULL, CLSCTX_SERVER, 
        IID_IAccPropServices, (void **) &pAccPropSvc);

    if (hr != S_OK || pAccPropSvc == NULL)
        return hr;

    // Set the Name property for the control.
    // Note: A hard-coded string is used here to keep the example simple.
    // Always use localizable string resources in your applications. 
    hr = pAccPropSvc->SetHwndPropStr(hwndCtl, OBJID_CLIENT, CHILDID_SELF, 
        PROPID_ACC_NAME, L"New Control Name");

    pAccPropSvc->Release();
    
    return hr;
}

// RemoveAnnotatedNameFromControl - Removes the annotated name from the 
// Name property of the IAccessible object for a control.
//
// hDlg - Handle of the dialog box that contains the control.
// hwndCtl - Handle of the control whose annotated name is to be removed.
HRESULT RemoveAnnotatedNameFromControl(HWND hDlg, HWND hwndCtl)
{
    HRESULT hr;

    IAccPropServices *pAccPropSvc = NULL;

    // Create an instance of the annotation manager and retrieve the 
    // IAccPropServices pointer.
    hr = CoCreateInstance(CLSID_AccPropServices, NULL, CLSCTX_SERVER, 
        IID_IAccPropServices, (void **) &pAccPropSvc);

    if (hr != S_OK || pAccPropSvc == NULL)
        return hr;

    // Remove the annotated name from the Name property for the control.
    MSAAPROPID propid = PROPID_ACC_NAME;
    hr = pAccPropSvc->ClearHwndProps(hwndCtl, OBJID_CLIENT, CHILDID_SELF, 
        &propid, 1);

    // Release the annotation manager.
    pAccPropSvc->Release();

    return hr;
}