Udostępnij za pośrednictwem


Implementacja dostawcy automatyzacji interfejsu użytkownika Server-Side

W tym temacie opisano sposób implementacji dostawcy automatyzacji interfejsu użytkownika Microsoft UI po stronie serwera dla niestandardowej kontrolki napisanej w języku C++. Zawiera on następujące sekcje:

Aby zobaczyć przykłady kodu pokazujące, jak zaimplementować dostawców po stronie serwera, zapoznaj się z tematami How-To dotyczącymi dostawców automatyzacji interfejsu użytkownika.

Struktura drzewa dostawcy

Należy zaimplementować dostawcę interfejsu użytkownika dla każdego elementu interfejsu użytkownika, który musi być dostępny dla klienta interfejsu użytkownika.

Na przykład każdy element musi implementować IRawElementProviderFragment, podczas gdy element główny aplikacji musi implementować IRawElementProviderFragmentRoot. Ponadto każdy element dostawcy powinien łączyć się z:

  • rodzic
  • poprzedni element dostawcy
  • następny element dostawcy
  • pierwszy dostawca podrzędny element
  • ostatnie dziecko dostawcy

Interfejsy dostawcy

Następujące interfejsy modelu obiektów składników (COM) zapewniają funkcje kontrolek niestandardowych. Aby zapewnić podstawowe funkcje, każdy dostawca automatyzacji interfejsu użytkownika musi zaimplementować co najmniej interfejs IRawElementProviderSimple. Interfejsy IRawElementProviderFragment i IRawElementProviderFragmentRoot są opcjonalne, ale należy zaimplementować dla elementów w złożonej kontrolce, aby zapewnić dodatkowe funkcje.

Interfejs Opis
IRawElementProviderSimple Udostępnia podstawowe funkcje kontrolki hostowanej w oknie, w tym obsługę wzorców i właściwości kontrolek.
IRawElementProviderFragment Dodaje funkcje elementu w złożonej kontrolce, w tym nawigowanie w fragmentcie, ustawianie fokusu i zwracanie prostokąta ograniczenia elementu.
IRawElementProviderFragmentRoot Dodaje funkcje elementu głównego w złożonej kontrolce, w tym lokalizowanie elementu podrzędnego na określonych współrzędnych i ustawianie stanu koncentracji uwagi dla całej kontrolki.

 

Notatka

W API automatyzacji interfejsu użytkownika dla kodu zarządzanego te interfejsy tworzą hierarchię dziedziczenia. Nie jest tak w języku C++, gdzie interfejsy są całkowicie oddzielone.

 

Następujące interfejsy zapewniają dodatkowe funkcje, ale implementacja jest opcjonalna.

Interfejs Opis
IRawElementProviderAdviseEvents Umożliwia dostawcy śledzenie żądań dotyczących zdarzeń.
IRawElementProviderHwndOverride Umożliwia zmiana położenia elementów opartych na oknach w drzewie automatyzacji interfejsu użytkownika fragmentu.

 

Wymagane funkcje dla dostawców automatyzacji interfejsu użytkownika

Aby komunikować się z automatyzacją interfejsu użytkownika, kontrolka musi zaimplementować główne obszary funkcjonalności opisane w poniższej tabeli.

Funkcjonalność Implementacja
Uwidaczniaj dostawcę w usłudze Automatyzacja interfejsu użytkownika. W odpowiedzi na komunikat WM_GETOBJECT wysłany do okna sterowania, należy zwrócić obiekt implementujący IRawElementProviderSimple. W przypadku fragmentów musi to być dostawca dla korzenia fragmentu.
Podaj wartości właściwości. Zaimplementuj IRawElementProviderSimple::GetPropertyValue, aby podać lub zastąpić wartości.
Umożliwia klientowi interakcję z kontrolką. Zaimplementuj interfejsy, które obsługują każdy odpowiedni wzorzec sterowania, taki jak IInvokeProvider. Zwróć tych dostawców wzorców sterowania w implementacji IRawElementProviderSimple::GetPatternProvider.
Wywoływanie zdarzeń. UiaRaiseAutomationEventmetody IProxyProviderWinEventSink.
Włącz nawigację i koncentrację w fragmencie. Zaimplementuj IRawElementProviderFragment dla każdego elementu w ramach fragmentu. Nie jest to konieczne w przypadku elementów, które nie są częścią fragmentu.
Umożliw ustawianie ostrości i lokalizowanie elementów podrzędnych w fragmencie. Zaimplementuj IRawElementProviderFragmentRoot. Nie jest to konieczne w przypadku elementów, które nie są fragmentami korzeni.

 

Wartości właściwości

Dostawcy automatyzacji interfejsu użytkownika dla kontrolek niestandardowych muszą obsługiwać określone właściwości, które mogą być używane przez automatyzację interfejsu użytkownika i aplikacje klienckie. W przypadku elementów hostowanych w systemie Windows automatyzacja interfejsu użytkownika może pobrać niektóre właściwości od domyślnego dostawcy okien, ale musi uzyskać inne od dostawcy niestandardowego.

Zazwyczaj dostawcy kontrolek opartych na oknach nie muszą udostępniać następujących właściwości, które są identyfikowane przez PROPERTYID:

Właściwość RuntimeId prostego elementu lub korzenia fragmentu hostowanego w oknie jest uzyskiwana z okna. Jednak elementy fragmentu poniżej korzenia, takie jak elementy listy w polu listy, muszą podać własne identyfikatory. Aby uzyskać więcej informacji, zobacz IRawElementProviderFragment::GetRuntimeId.

Właściwość IsKeyboardFocusable powinna być zwracana dla dostawców hostowanych w kontrolce Windows Forms. W takim przypadku domyślny dostawca okien może nie być w stanie pobrać poprawnej wartości.

Właściwość Name jest zwykle dostarczana przez dostawcę hosta.

Zdarzenia od dostawców

Dostawcy automatyzacji interfejsu użytkownika powinni zgłaszać zdarzenia w celu powiadamiania aplikacji klienckich o zmianach w stanie interfejsu użytkownika. Następujące funkcje służą do zgłaszania zdarzeń.

Funkcja Opis
UiaRaiseAutomationEvent Wywołuje różne zdarzenia, w tym zdarzenia wyzwalane przez wzorce kontrolek.
UiaRaiseAutomationPropertyChangedEvent Zgłasza zdarzenie, gdy właściwość automatyzacji interfejsu użytkownika uległa zmianie.
UiaRaiseStructureChangedEvent Zgłasza zdarzenie, gdy struktura drzewa automatyzacji interfejsu użytkownika uległa zmianie, na przykład przez usunięcie lub dodanie elementu.

 

Celem zdarzenia jest powiadomienie klienta o czymś, co ma miejsce w interfejsie użytkownika. Dostawcy powinni zgłosić zdarzenie niezależnie od tego, czy zmiana została zainicjowana przez wejście użytkownika, czy przez aplikację kliencką za pomocą automatyzacji interfejsu użytkownika. Na przykład zdarzenie zidentyfikowane przez UIA_Invoke_InvokedEventId powinno być zgłaszane za każdym razem, gdy jest wywoływany element sterujący, za pośrednictwem bezpośredniego wejścia przez użytkownika lub przez aplikację kliencką wywołującą IUIAutomationInvokePattern::Invoke.

Aby zoptymalizować wydajność, dostawca może selektywnie zgłaszać zdarzenia lub nie zgłaszać żadnych, jeśli żadna aplikacja kliencka nie jest zarejestrowana w celu ich odbierania. Następujące elementy interfejsu API są używane do optymalizacji.

API element Opis
UiaClientsAreListening Ta funkcja określa, czy wszystkie aplikacje klienckie zasubskrybowały zdarzenia automatyzacji interfejsu użytkownika.
IRawElementProviderAdviseEvents Zaimplementowanie tego interfejsu na głównym komponencie fragmentu umożliwia powiadamianie dostawcy, gdy klienci rejestrują i wyrejestrowują programy obsługi zdarzeń dla zdarzeń fragmentu.

 

Notatka

Podobnie jak w przypadku implementowania liczenia odwołań w programowaniu COM, dostawcy automatyzacji interfejsu użytkownika muszą traktować metody IRawElementProviderAdviseEvents::AdviseEventAdded i AdviseEventRemoved tak, jak metody IUnknown::AddRef i Release interfejsu IUnknown. Tak długo, jak AdviseEventAdded został wywołany więcej razy niż AdviseEventRemoved dla określonego zdarzenia lub właściwości, dostawca powinien nadal zgłaszać te zdarzenia, ponieważ niektórzy klienci nadal nasłuchują. Alternatywnie dostawcy automatyzacji interfejsu użytkownika mogą używać funkcji UiaClientsAreListening, aby określić, czy co najmniej jeden klient nasłuchuje, a jeśli tak, zgłasza wszystkie odpowiednie zdarzenia.

 

Panel Nawigacyjny Dostawcy

Dostawcy prostych kontrolek, takich jak przycisk niestandardowy hostowany w oknie, nie muszą obsługiwać nawigacji w drzewie automatyzacji interfejsu użytkownika. Nawigacja do i z elementu jest obsługiwana przez domyślnego dostawcę dla okna hosta, który jest określony w implementacji IRawElementProviderSimple::HostRawElementProvider. Podczas implementowania dostawcy dla złożonej kontrolki niestandardowej należy jednak obsługiwać nawigację między węzłem głównym fragmentu i jego elementami podrzędnymi oraz między węzłami równorzędnymi.

Notatka

Elementy fragmentu inne niż element główny muszą zwracać NULL z HostRawElementProvider, ponieważ nie są one bezpośrednio hostowane w oknie, a żaden domyślny dostawca nie może obsługiwać nawigacji do nich i od nich.

 

Struktura fragmentu jest określana przez implementację IRawElementProviderFragment::Navigate. Dla każdego możliwego kierunku z każdego fragmentu ta metoda zwraca obiekt dostawcy dla elementu w tym kierunku. Jeśli w tym kierunku nie ma żadnego elementu, metoda zwraca wartość null.

Korzeń fragmentu obsługuje nawigację tylko do elementów podrzędnych. Na przykład pole listy zwraca pierwszy element na liście, gdy kierunek jest NavigateDirection_FirstChild, i zwraca ostatni element, gdy kierunek jest NavigateDirection_LastChild. Root fragment nie obsługuje nawigacji do elementu nadrzędnego lub elementów równorzędnych; To jest obsługiwane przez dostawcę okna hosta.

Elementy fragmentu, które nie są elementem głównym, muszą obsługiwać nawigację do elementu nadrzędnego oraz do wszystkich elementów równorzędnych i elementów podrzędnych, które mają.

Przypisywanie nowego rodzica

Okna wyskakujące są w rzeczywistości oknami najwyższego poziomu i domyślnie są wyświetlane w drzewie automatyzacji interfejsu użytkownika jako dzieci pulpitu. W wielu przypadkach okna podręczne są jednak logicznie dziećmi innych elementów sterujących. Na przykład lista rozwijana pola kombi jest logicznie elementem podrzędnym pola kombi. Podobnie, podręczne okno menu jest logicznie elementem podrzędnym menu. Automatyzacja interfejsu użytkownika zapewnia obsługę przypisywania nowego rodzica do okna podręcznego, aby wyglądało na element podrzędny skojarzonej kontrolki.

Aby przypisać nowego rodzica do okna podręcznego:

  1. Utwórz dostawcę dla okna podręcznego. Wymaga to, aby klasa okna podręcznego była znana wcześniej.
  2. Zaimplementuj wszystkie właściwości i wzorce sterowania dla tego wyskakującego okienka, jakby była to samodzielna kontrolka.
  3. Zaimplementuj właściwość IRawElementProviderSimple::HostRawElementProvider, aby zwracała wartość uzyskaną z UiaHostProviderFromHwnd, gdzie parametr jest uchwytem okna wyskakującego.
  4. Zaimplementuj IRawElementProviderFragment::Navigate dla okna podręcznego i jego elementu nadrzędnego, aby nawigacja została prawidłowo obsłużona z logicznego elementu nadrzędnego do logicznych elementów podrzędnych oraz między równorzędnymi elementami podrzędnymi.

Gdy usługa Automatyzacja interfejsu użytkownika napotka okno podręczne, rozpoznaje, że nawigacja jest zastępowana z domyślnej, i pomija okno podręczne, gdy napotka je jako element podrzędny pulpitu. Zamiast tego węzeł jest osiągalny tylko za pośrednictwem fragmentu.

Przypisywanie nowego elementu nadrzędnego nie jest odpowiednie w przypadkach, w których kontrolka może hostować okno dowolnej klasy. Na przykład kontrolka typu rebar może hostować dowolny typ okna w pasmach. Aby obsłużyć te przypadki, automatyzacja interfejsu użytkownika obsługuje alternatywną formę relokacji okien, zgodnie z opisem w następnej sekcji.

Zmiana położenia dostawcy

Fragmenty automatyzacji interfejsu użytkownika mogą zawierać co najmniej dwa elementy, które są zawarte w oknie. Ponieważ każde okno posiada swojego domyślnego dostawcę, który traktuje okno jako element podrzędny okna zawierającego, drzewo automatyzacji interfejsu użytkownika będzie domyślnie pokazywać okna we fragmencie jako elementy podrzędne okna nadrzędnego. W większości przypadków jest to pożądane zachowanie, ale czasami może prowadzić do nieporozumień, ponieważ nie pasuje do logicznej struktury interfejsu użytkownika.

Dobrym przykładem jest kontrolka paska pomocniczego. Kontrolka typu rebar zawiera pasma, z których każde może z kolei zawierać kontrolkę opartą na oknach systemowych, taką jak pasek narzędzi, pole edycyjne lub pole kombi. Domyślny dostawca okien dla okna paska pomocniczego widzi okna sterowania pasmem jako elementy podrzędne, a dostawca paska pomocniczego widzi pasma jako elementy podrzędne. Ponieważ dostawca okien i dostawca prętów pracują razem i łączą swoje elementy podrzędne, zarówno pasy, jak i kontrolki oparte na oknach są wyświetlane jako elementy podrzędne kontrolki prętów. Logicznie jednak tylko paski powinny być wyświetlane jako elementy podrzędne kontrolki paska narzędzi, a każdy dostawca pasków powinien być powiązany z domyślnym dostawcą okien dla kontrolki, którą zawiera.

W tym celu dostawca główny fragmentu kontrolki paska pomocniczego uwidacznia zestaw elementów podrzędnych reprezentujących pasma. Każdy zespół ma jednego dostawcę, który może uwidaczniać właściwości i wzorce sterowania. W implementacji IRawElementProviderSimple::HostRawElementProviderdostawca pasma zwraca domyślnego dostawcę okna dla okna kontrolnego, którego uzyskuje poprzez wywołanie UiaHostProviderFromHwnd, przekazując dojście okna kontrolki (HWND). Na koniec dostawca główny fragmentu dla paska pomocniczego implementuje interfejs IRawElementProviderHwndOverride oraz w implementacji interfejsu IRawElementProviderHwndOverride::GetOverrideProviderForHwndzwraca odpowiedniego dostawcę pasm dla kontrolki zawartej w określonym oknie.

Rozłączanie dostawców

Aplikacje zazwyczaj tworzą kontrolki, gdy są potrzebne, a następnie je usuwają. Po zniszczeniu kontrolki zasoby dostawcy automatyzacji interfejsu użytkownika skojarzone z kontrolką powinny zostać zwolnione przez wywołanie UiaDisconnectProvider.

Podobnie aplikacja powinna używać funkcji UiaDisconnectAllProviders, aby zwolnić wszystkie zasoby automatyzacji interfejsu użytkownika przechowywane przez wszystkich dostawców w aplikacji przed zamknięciem.

Przewodnik programisty dostawcy automatyzacji interfejsu użytkownika