Udostępnij przez


Upewnienie się, że elementy interfejsu użytkownika są poprawnie nazwane

W tym temacie opisano prawidłowy sposób określania nazw elementów interfejsu użytkownika w aplikacjach Platformy Microsoft Win32, dzięki czemu usługa Microsoft Active Accessibility może dokładnie uwidocznić nazwy aplikacjom klienckim za pomocą właściwości IAccessibleName.

Informacje w tej sekcji mają zastosowanie tylko do usługi Microsoft Active Accessibility. Nie dotyczy aplikacji korzystających z automatyzacji interfejsu użytkownika firmy Microsoft lub opartych na językach znaczników, takich jak HTML, Dynamic HTML (DHTML) lub XML.

Przegląd

W usłudze Microsoft Active Accessibility każdy element interfejsu użytkownika w aplikacji jest reprezentowany przez obiekt, który udostępnia interfejs IAccessible. Aplikacje klienckie używają właściwości i metod interfejsu IAccessible do interakcji z elementem interfejsu użytkownika i pobierania informacji o nim. Jedną z najważniejszych właściwości uwidocznionych przez interfejs IAccessible jest właściwość Name. Aplikacje klienckie opierają się na właściwości Name, aby znaleźć, zidentyfikować lub ogłosić element interfejsu użytkownika użytkownikowi. Jeśli usługa Microsoft Active Accessibility nie może prawidłowo uwidocznić właściwości Name określonego elementu interfejsu użytkownika, aplikacje klienckie nie będą mogły przedstawić tego elementu interfejsu użytkownika użytkownikowi, a element interfejsu użytkownika będzie niedostępny dla użytkowników niepełnosprawnych.

Jak nieprawidłowe nazewnictwo powoduje problemy

Aby zilustrować problemy spowodowane nieprawidłowym nazewnictwem elementów interfejsu użytkownika, rozważ formularz wprowadzania nazwy pokazany na poniższej ilustracji.

ilustracja prostego formularza do wprowadzania pierwszego i nazwiska

Mimo że elementy interfejsu użytkownika w formularzu wyglądają dobrze, implementacja programowa jest niepoprawna. W kliencie Microsoft Active Accessibility, takim jak czytnik ekranu, właściwość Name górnej kontrolki edycji to "Nazwisko:", a właściwość Name dolnej kontrolki edycji jest pustym ciągiem (""). Czytnik zawartości ekranu odczyta górną kontrolkę edycji jako "Nazwisko", chociaż użytkownik powinien wpisać imię. Czytnik zawartości ekranu odczytuje drugą kontrolkę edycji jako "bez nazwy", więc użytkownik nie będzie miał pojęcia, co należy wpisać w drugiej kontrolce edycji. Czytnik zawartości ekranu nie może pomóc użytkownikowi w wprowadzeniu danych do tego prostego formularza.

Innym problemem z formularzem jest to, że żadne skróty nie są przypisane do żadnej z kontrolek edycji. Użytkownik jest zmuszony do naciśnięcia klawisza Tab, aby przejść do kontrolek, lub użycia myszy.

W poniższych sekcjach opisano źródło tych problemów i podano wskazówki dotyczące ich poprawiania.

Jak usługa MSAA pobiera właściwość Name

Usługa Microsoft Active Accessibility pobiera właściwość Name ciąg z różnych lokalizacji w zależności od typu elementu interfejsu użytkownika. W przypadku większości elementów interfejsu użytkownika, które mają skojarzony tekst okna, funkcja Microsoft Active Accessibility używa tekstu okna jako ciągu właściwości Name. Przykłady tego typu elementu interfejsu użytkownika obejmują elementy sterujące, takie jak przyciski, elementy menu i podpowiedzi.

W przypadku następujących kontrolek funkcja Microsoft Active Accessibility ignoruje tekst okna i wyszukuje statyczną etykietę tekstową (lub etykietę pola grupy) bezpośrednio poprzedzającą kontrolkę w kolejności tabulacji.

  • Pola kombi
  • Selektory dat i godzin
  • Kontrolki edycji i edycji wzbogaconej
  • Kontrolki adresów IP
  • Listy wyboru
  • Widoki listy
  • Paski postępu
  • Paski przewijania
  • Kontrolki statyczne, które mają styl SS_ICON lub SS_BITMAP
  • Paski śledzenia
  • Widoki drzewa

Jeśli powyższe kontrolki nie są dołączone do etykiet tekstowych statycznych lub jeśli etykiety nie są poprawnie zaimplementowane, usługa Microsoft Active Accessibility nie może podać poprawnej właściwości Name dla aplikacji klienckich.

Większość wcześniej wspomnianych kontrolek rzeczywiście ma skojarzony tekst okna. Edytor zasobów automatycznie generuje tekst okna, który składa się z ogólnego ciągu, takiego jak "edit1" lub "listbox3". Mimo że deweloperzy mogą zastąpić wygenerowany tekst okna bardziej zrozumiałym tekstem, większość nigdy nie robi. Ponieważ wygenerowany tekst okna nie ma znaczenia dla użytkownika, funkcja Microsoft Active Accessibility ignoruje go i używa towarzyszącej etykiety tekstowej statycznej.

Jak znaleźć i rozwiązać problemy z nazewnictwem

W formularzu wpisu nazwy pokazanym w temacie Jak niepoprawne nazewnictwo powoduje problemy, przyczyną problemów jest to, że kolejność tabulacji kontrolek jest niepoprawna. Badanie interfejsu użytkownika za pomocą narzędzia do testowania, takiego jak Inspect, spowoduje ujawnienie problemów z hierarchią obiektów. Poniższy zrzut ekranu przedstawia uszkodzoną hierarchię obiektów formularza wpisu nazwy, jak to widać w Inspect.

zrzut ekranu narzędzia inspekcji przedstawiający niepoprawną hierarchię obiektów formularza wpisu nazwy

Na poprzednim zrzucie ekranu zwróć uwagę, że hierarchia obiektów nie jest zgodna ze strukturą kontrolek, jak są one wyświetlane w interfejsie użytkownika formularza wprowadzania nazwy. Należy również zauważyć, że Inspect przypisał nieprawidłową nazwę do przedostatniego elementu (jest to kontrolka edycji do wprowadzania imienia i powinna być oznaczona jako "Imię:"). Na koniec zwróć uwagę, że funkcja Inspekcja nie może odnaleźć nazwy ostatniego elementu (jest to kontrolka edycji do wprowadzania nazwiska i powinna mieć nazwę "Nazwisko:".

W poniższym przykładzie pokazano zawartość pliku zasobu dla formularza wpisu nazwy. Zwróć uwagę, że kolejność tabulacji nie jest zgodna ze strukturą logiczną kontrolek, jak są przedstawiane w interfejsie użytkownika. Zwróć również uwagę, że dla dwóch kontrolek edycji nie określono żadnych skrótów.

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

Aby rozwiązać problemy z formularzem wprowadzania nazwy, należy edytować plik zasobu (rc) w celu określenia skrótów klawiaturowych, a kontrolki powinny zostać umieszczone w następującej kolejności:

  1. Etykieta statyczna tekstu "&Imię:".
  2. Kontrolka edycji do wprowadzania pierwszej nazwy (IDC_EDIT1).
  3. Etykieta statyczna "&Nazwisko:".
  4. Kontrolka edycji do wprowadzania nazwiska (IDC_EDIT2).
  5. Domyślny przycisk wypychania "OK".

W poniższym przykładzie pokazano poprawiony plik zasobu dla formularza wpisu nazwy:

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

Aby wprowadzić poprawki do pliku zasobu, możesz edytować plik bezpośrednio lub użyć narzędzia do ustawiania kolejności tabulacji w programie Microsoft Visual Studio. Aby uzyskać dostęp do narzędzia Kolejność tabulacji w programie Visual Studio, naciśnij CTRL+D lub wybierz pozycję Kolejność tabulacji z menu Format.

Po skorygowaniu i odbudowaniu aplikacji interfejs użytkownika formularza wpisu nazw będzie wyglądać tak samo jak wcześniej. Jednak funkcja Microsoft Active Accessibility będzie teraz dostarczać poprawne właściwości nazwy dla aplikacji klienckich i poprawnie ustawiać fokus, gdy użytkownik naciśnie skróty klawiaturowe ALT+F lub ALT+L. Ponadto Inspekcja pokaże poprawną hierarchię obiektów, jak pokazano na poniższym zrzucie ekranu.

zrzut ekranu narzędzia dostępnego eksploratora przedstawiający poprawną hierarchię obiektów formularza wprowadzania nazwy

Jak poprawnie nazwać pasek śledzenia

Podczas definiowania paska śledzenia (lub suwaka) upewnij się, że główna statyczna etykieta tekstowa paska śledzenia jest wyświetlana przed paskiem trackbar i że etykiety tekstu statycznego dla minimalnych i maksymalnych zakresów są wyświetlane po pasku trackbar. Należy pamiętać, że usługa Microsoft Active Accessibility używa statycznej etykiety tekstowej, która bezpośrednio poprzedza kontrolkę jako właściwość Name dla kontrolki. Umieszczenie głównej statycznej etykiety tekstowej bezpośrednio przed suwakiem oraz pozostałych etykiet za nim zapewnia, że Microsoft Active Accessibility udostępnia właściwość Name poprawnie klientowi.

Poniższa ilustracja przedstawia typowy pasek trackbar z główną statyczną etykietą tekstową o nazwie "Speed" i statycznymi etykietami tekstowymi dla minimalnych ("min") i maksymalnych zakresów ("max").

ilustracja kontrolki suwaka z główną etykietą oraz etykietami dla minimalnego i maksymalnego zakresu

W poniższym przykładzie pokazano prawidłowy sposób definiowania paska śledzenia i jego statycznych etykiet tekstowych w pliku zasobu:

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

Jak używać niewidocznych etykiet do nazwania kontrolek

Nie zawsze jest możliwe lub pożądane, aby mieć widoczną etykietę dla każdej kontrolki. Na przykład czasami dodawanie etykiet może spowodować niepożądane zmiany w wyglądzie interfejsu użytkownika. W takim przypadku można użyć niewidocznych etykiet. Funkcja Microsoft Active Accessibility nadal będzie wykrywać tekst skojarzony z niewidoczną etykietą, ale etykieta nie będzie widoczna ani nie będzie zakłócać wizualnego interfejsu użytkownika.

Podobnie jak w przypadku widocznych etykiet, niewidoczna etykieta musi znajdować się bezpośrednio przed kontrolką w kolejności tabulacji. Aby utworzyć etykietę niewidoczną w pliku zasobu (rc), dodaj NOT WS_VISIBLE lub |~WS_VISIBLE do części stylu kontrolki tekstu statycznego. Jeśli używasz edytora zasobów w programie Visual Studio, możesz ustawić właściwość Visible na wartość False.

Jak używać bezpośredniej adnotacji do określenia właściwości nazwanej

Domyślne serwery proxy zawarte w składniku środowiska uruchomieniowego Microsoft Active Accessibility Oleacc.dllautomatycznie udostępniają obiekt IAccessible dla wszystkich standardowych kontrolek systemu Windows. Jeśli dostosujesz standardową kontrolkę systemu Windows, domyślne serwery proxy dołożą wszelkich starań, aby dokładnie przedstawić wszystkie właściwości IAccessible dla twojej dostosowanej kontrolki. Należy dokładnie przetestować spersonalizowaną kontrolę, aby upewnić się, że domyślne serwery proxy zapewniają dokładne i kompletne wartości właściwości. Jeśli testowanie ujawnia niedokładne lub niekompletne wartości właściwości, możesz użyć techniki adnotacji dynamicznej nazywanej adnotacją bezpośrednią, aby podać poprawne wartości właściwości i dodać te, które są brakujące.

Pamiętaj, że adnotacja dynamiczna nie dotyczy tylko kontrolek obsługiwanych przez serwery proxy microsoft Active Accessibility. Można go również użyć do modyfikowania lub udostępniania właściwości dowolnej kontrolki, która zapewnia własną implementację IAccessible.

Ta sekcja koncentruje się na korzystaniu z adnotacji bezpośredniej w celu zapewnienia poprawnej wartości właściwości Name obiektu IAccessible dla kontrolki. Możesz również użyć adnotacji bezpośredniej, aby podać inne wartości właściwości. Ponadto dostępne są inne techniki adnotacji dynamicznych obok adnotacji bezpośrednich, a funkcje i możliwości interfejsu API adnotacji dynamicznych wykraczają daleko poza to, co opisano w tej sekcji. Aby uzyskać więcej informacji na temat adnotacji dynamicznych, zobacz interfejs API adnotacji dynamicznej .

Kroki dodawania adnotacji do właściwości Name

Użycie adnotacji bezpośredniej w celu zmiany właściwości nazwy kontrolki obejmuje następujące kroki.

  1. Dołącz następujące pliki nagłówka:

    • Initguid.h
    • Oleacc.h

    Notatka

    Aby zdefiniować identyfikatory GUID, należy uwzględnić Initguid.h przed plikiem Oleacc.h w tym samym pliku.

     

  2. Zainicjuj bibliotekę modelu obiektów składników (COM), wywołując funkcję CoInitializeEx, zazwyczaj podczas procesu inicjowania aplikacji.

  3. Wkrótce po utworzeniu kontrolki docelowej (zazwyczaj podczas komunikatu WM_INITDIALOG) utwórz wystąpienie menedżera adnotacji i uzyskaj wskaźnik do jego wskaźnika IAccPropServices wskaźnika.

  4. Zanotuj właściwość nazwy kontrolki docelowej przy użyciu metody IAccPropServices::SetHwndPropStr.

  5. Zwolnij wskaźnik IAccPropServices.

  6. Zanim kontrolka docelowa zostanie zniszczona (zazwyczaj podczas obsługi komunikatu WM_DESTROY), utwórz wystąpienie menedżera adnotacji i uzyskaj wskaźnik do jego interfejsu IAccPropServices.

  7. Użyj metody IAccPropServices::ClearHwndProps, aby wyczyścić adnotacje właściwości o nazwie Name z docelowej kontrolki.

  8. Zwolnij wskaźnik IAccPropServices.

  9. Zanim aplikacja zakończy działanie (zazwyczaj podczas przetwarzania komunikatu WM_DESTROY), zwolnij bibliotekę COM, wywołując funkcję CoUninitialize.

Funkcja IAccPropServices::SetHwndPropStr przyjmuje pięć parametrów. Pierwsze trzy —hwnd, idObjecti idChild— łączą się w celu zidentyfikowania kontrolki. Czwarty parametr, idProp, określa identyfikator właściwości, która ma zostać zmieniona. Aby zmienić właściwość Name, ustaw idProp na PROPID_ACC_NAME. (Aby uzyskać listę innych właściwości, które można ustawić za pomocą adnotacji bezpośredniej, zobacz Using Direct Annotation.) Ostatni parametr SetHwndPropStr, str, jest nowym ciągiem używanym jako właściwość Name.

Przykład dodawania adnotacji do właściwości Name

Poniższy przykładowy kod pokazuje, jak użyć adnotacji bezpośredniej, aby zmienić właściwość nazwa obiektu IAccessible dla kontrolki. Aby zachować prostotę, w przykładzie użyto zakodowanego ciągu ("Nowa nazwa kontrolki") w celu ustawienia właściwości Name. Zakodowane ciągi nie powinny być używane w ostatecznej wersji aplikacji, ponieważ nie mogą być zlokalizowane. Zamiast tego zawsze ładuj ciągi z pliku zasobu. Ponadto w przykładzie nie są wyświetlane wywołania funkcji CoInitializeEx i 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;
}