Condividi tramite


Implementare un provider di automazione interfaccia utente Server-Side

Questo argomento descrive come implementare un provider di automazione interfaccia utente Microsoft lato server per un controllo personalizzato scritto in C++. Contiene le sezioni seguenti:

Per esempi di codice che illustrano come implementare provider lato server, vedere How-To Topics for UI Automation Providers.

Struttura ad albero del fornitore

È necessario implementare un provider UIA per ogni elemento dell'interfaccia utente che deve essere accessibile a un client UIA.

Ad esempio, ogni elemento deve implementare IRawElementProviderFragment mentre l'elemento radice dell'applicazione deve implementare IRawElementProviderFragmentRoot. Inoltre, ogni elemento del provider deve essere collegato a:

  • genitore
  • elemento del fornitore precedente
  • elemento fornitore successivo
  • primo figlio fornitore
  • ultimo fornitore figlio

Interfacce fornitore

Le interfacce COM (Component Object Model) seguenti forniscono funzionalità per i controlli personalizzati. Per fornire funzionalità di base, ogni provider di UI Automation deve implementare almeno l'interfaccia IRawElementProviderSimple. Le interfacce IRawElementProviderFragment e IRawElementProviderFragmentRoot sono facoltative, ma devono essere implementate per gli elementi in un controllo complesso per fornire funzionalità aggiuntive.

Interfaccia Descrizione
IRawElementProviderSimple Fornisce funzionalità di base per un controllo ospitato in una finestra, incluso il supporto per i pattern di controllo e le proprietà.
IRawElementProviderFragment Aggiunge funzionalità per un elemento in un controllo complesso, tra cui lo spostamento nel frammento, l'impostazione dello stato attivo e la restituzione del rettangolo di delimitazione dell'elemento.
IRawElementProviderFragmentRoot Aggiunge funzionalità per l'elemento radice in un controllo complesso, inclusa l'individuazione di un elemento figlio in corrispondenza delle coordinate specificate e l'impostazione dello stato attivo per l'intero controllo.

 

Nota

Nell'API di automazione interfaccia utente per il codice gestito queste interfacce formano una gerarchia di ereditarietà. Questo non è il caso in C++, in cui le interfacce sono completamente separate.

 

Le interfacce seguenti forniscono funzionalità aggiunte, ma l'implementazione è facoltativa.

Interfaccia Descrizione
IRawElementProviderAdviseEvents Consente al provider di tenere traccia delle richieste di eventi.
IRawElementProviderHwndOverride Abilita il riposizionamento degli elementi basati su finestra nell'albero di automazione interfaccia utente di un frammento.

 

Funzionalità necessarie per i provider di automazione interfaccia utente

Per comunicare con Automazione interfaccia utente, il controllo deve implementare le principali aree di funzionalità descritte nella tabella seguente.

Funzionalità Implementazione
Esporre il provider all'automazione interfaccia utente. In risposta a un messaggio di WM_GETOBJECT inviato alla finestra di controllo, restituire l'oggetto che implementa IRawElementProviderSimple. Per i frammenti, questo deve essere il provider per la radice del frammento.
Specificare i valori delle proprietà. Implementare IRawElementProviderSimple::GetPropertyValue per fornire o eseguire l'override dei valori.
Abilitare il client per interagire con il controllo . Implementare interfacce che supportano ogni pattern di controllo appropriato, ad esempio IInvokeProvider. Restituire questi provider di pattern di controllo nell'implementazione di IRawElementProviderSimple::GetPatternProvider.
Generare eventi. UiaRaiseAutomationEvent, metodi di IProxyProviderWinEventSink.
Abilitare lo spostamento e la messa a fuoco in un frammento. Implementare IRawElementProviderFragment per ogni elemento all'interno del frammento. Non necessario per gli elementi che non fanno parte di un frammento.
Abilitare la messa a fuoco e l'individuazione degli elementi figli in un frammento. Implementare IRawElementProviderFragmentRoot. Non necessario per gli elementi che non sono radici frammentarie.

 

Valori delle proprietà

I provider di automazione interfaccia utente per i controlli personalizzati devono supportare determinate proprietà che possono essere usate dall'automazione interfaccia utente e dalle applicazioni client. Per gli elementi ospitati in finestre, Automazione interfaccia utente può recuperare alcune proprietà dal provider di finestre predefinito, ma deve ottenere altri dal provider personalizzato.

In genere, i provider per i controlli basati su finestre non devono fornire le proprietà seguenti identificate da PROPERTYID:

La proprietà RuntimeId di una radice di elemento o frammento semplice ospitata in una finestra viene ottenuta dalla finestra. Tuttavia, gli elementi del frammento sotto la radice, ad esempio gli elementi di elenco in una casella di riepilogo, devono fornire i propri identificatori. Per altre informazioni, vedere IRawElementProviderFragment::GetRuntimeId.

La proprietà IsKeyboardFocusable deve essere restituita per i provider ospitati in un controllo Windows Forms. In questo caso, il provider di finestre predefinito potrebbe non essere in grado di recuperare il valore corretto.

La proprietà Name viene in genere fornita dal provider host.

Eventi dai fornitori

I provider di automazione interfaccia utente devono generare eventi per notificare alle applicazioni client modifiche nello stato dell'interfaccia utente. Per generare eventi vengono usate le funzioni seguenti.

Funzione Descrizione
UiaRaiseAutomationEvent Genera vari eventi, inclusi gli eventi attivati dai pattern di controllo.
UiaRaiseAutomationPropertyChangedEvent Genera un evento quando viene modificata una proprietà di automazione interfaccia utente.
UiaRaiseStructureChangedEvent Genera un evento quando la struttura dell'albero di automazione interfaccia utente è stata modificata, ad esempio rimuovendo o aggiungendo un elemento.

 

Lo scopo di un evento è notificare al client un evento che si verifica nell'interfaccia utente. I provider devono generare un evento indipendentemente dal fatto che la modifica sia stata attivata dall'input dell'utente o da un'applicazione client tramite automazione interfaccia utente. Ad esempio, l'evento identificato da UIA_Invoke_InvokedEventId deve essere generato ogni volta che il controllo viene richiamato, tramite input utente diretto o dall'applicazione client che chiama IUIAutomationInvokePattern::Invoke.

Per ottimizzare le prestazioni, un provider può scegliere di generare eventi in modo selettivo oppure non generare eventi affatto se nessuna applicazione client è registrata per riceverli. Per l'ottimizzazione vengono usati gli elementi API seguenti.

Elemento API Descrizione
UiaClientsAreListening Questa funzione verifica se le applicazioni client hanno sottoscritto eventi di automazione interfaccia utente.
IRawElementProviderAdviseEvents L'implementazione di questa interfaccia in una radice di frammento consente al provider di essere avvisato quando i client registrano e annullano la registrazione dei gestori di eventi per gli eventi nel frammento.

 

Nota

Analogamente all'implementazione del conteggio dei riferimenti nella programmazione COM, è importante che i provider di automazione interfaccia utente considerino i metodi IRawElementProviderAdviseEvents::AdviseEventAdded e AdviseEventRemoved come i metodi IUnknown::AddRef e Release dell'interfaccia IUnknown. Fino a quando AdviseEventAdded sia stato chiamato più volte rispetto a AdviseEventRemoved per un evento o una proprietà specifica, il fornitore deve continuare a sollevare eventi corrispondenti, perché alcuni client sono ancora in ascolto. In alternativa, i provider di automazione interfaccia utente possono usare la funzioneuiaClientsAreListeningper determinare se almeno un client è in ascolto e, in tal caso, generare tutti gli eventi appropriati.

 

Navigazione del provider

I provider per controlli semplici, ad esempio un pulsante personalizzato ospitato in una finestra, non devono supportare la navigazione nell'albero di automazione interfaccia utente. La navigazione da e verso l'elemento viene gestita dal provider predefinito per la finestra host, specificata nell'implementazione di IRawElementProviderSimple::HostRawElementProvider. Quando si implementa un provider per un controllo personalizzato complesso, tuttavia, è necessario supportare lo spostamento tra il nodo radice del frammento e i relativi discendenti e tra nodi di pari livello.

Nota

Gli elementi di un frammento diverso dalla radice devono restituire NULL da HostRawElementProvider, perché non sono ospitati direttamente in una finestra e nessun provider predefinito può supportare la navigazione da e verso di essi.

 

La struttura del frammento è determinata dall'implementazione di IRawElementProviderFragment::Navigate. Per ogni direzione possibile da ogni frammento, questo metodo restituisce l'oggetto provider per l'elemento in tale direzione. Se non è presente alcun elemento in tale direzione, il metodo restituisce NULL.

La radice del frammento supporta la navigazione solo agli elementi figli. Ad esempio, una casella di riepilogo restituisce il primo elemento dell'elenco quando la direzione è NavigateDirection_FirstChilde restituisce l'ultimo elemento quando la direzione è NavigateDirection_LastChild. La radice del frammento non supporta lo spostamento a un elemento padre o a elementi di pari livello; la navigazione è gestita dal provider della finestra host.

Gli elementi di un frammento che non sono la radice devono supportare la navigazione verso l'elemento padre, così come ai fratelli e ai figli che possiedono.

Assegnare un nuovo padre

Le finestre popup sono in realtà finestre di primo livello e, per impostazione predefinita, vengono visualizzate nell'albero di Automazione interfaccia utente come elementi figlio del desktop. In molti casi, tuttavia, le finestre popup sono elementi figlio logici di un altro controllo. Ad esempio, l'elenco a discesa di una casella combinata è logicamente un elemento figlio della casella combinata. Analogamente, una finestra a comparsa del menu è logicamente un elemento figlio del menu. UI Automation offre supporto per assegnare un nuovo elemento padre a una finestra popup in modo che appaia come un elemento figlio del controllo associato.

Per assegnare un nuovo genitore a una finestra popup:

  1. Creare un provider per la finestra popup. Ciò richiede che la classe della finestra popup sia nota in anticipo.
  2. Implementare tutte le proprietà e i pattern di controllo come di consueto per tale popup, come se fosse un controllo di per sé.
  3. Implementare la proprietà IRawElementProviderSimple::HostRawElementProvider in modo che restituisca il valore ottenuto da UiaHostProviderFromHwnd, dove il parametro è l'handle di finestra della finestra popup.
  4. Implementare IRawElementProviderFragment::Navigate per la finestra popup e il relativo elemento padre in modo che lo spostamento venga gestito correttamente dall'elemento padre logico agli elementi figlio logici e tra elementi figlio di pari livello.

Quando Automazione Interfaccia Utente rileva la finestra popup, riconosce che la navigazione viene sovrascritta rispetto al valore predefinito e ignora la finestra popup quando viene incontrata come figlio del desktop. Al contrario, il nodo è raggiungibile solo tramite il frammento.

L'assegnazione di un nuovo elemento padre non è adatta ai casi in cui un controllo può ospitare una finestra di qualsiasi classe. Ad esempio, un controllo rebar può ospitare qualsiasi tipo di finestra nelle relative bande. Per gestire questi casi, Automazione interfaccia utente supporta una forma alternativa di rilocazione delle finestre, come descritto nella sezione successiva.

Riposizionamento del provider

I frammenti di automazione interfaccia utente possono contenere due o più elementi contenuti in una finestra. Poiché ogni finestra ha un proprio provider predefinito che la considera figlio di una finestra contenitore, l'albero di Automazione interfaccia utente, per impostazione predefinita, mostrerà le finestre nel frammento come elementi figlio della finestra padre. Nella maggior parte dei casi questo comportamento è auspicabile, ma a volte può causare confusione perché non corrisponde alla struttura logica dell'interfaccia utente.

Un buon esempio di questo è un controllo rebar. Un controllo rebar contiene bande, ognuna delle quali può a sua volta contenere un controllo basato su finestra, ad esempio una barra degli strumenti, una casella di modifica o una casella combinata. Il provider di finestre predefinito per la finestra Rebar vede le finestre di controllo banda come figli e il provider della barra di riepilogo vede le bande come figli. Poiché il fornitore di finestre e il fornitore della barra degli strumenti lavorano insieme e combinano i relativi elementi figlio, sia le bande che i controlli basati su finestre vengono visualizzati come elementi figlio del controllo della barra degli strumenti. Logicamente, tuttavia, solo le bande dovrebbero apparire come elementi figlio del controllo rebar e ciascun provider di bande deve essere associato al provider di finestre predefinito per il controllo che contiene.

A tale scopo, il provider radice del frammento per il controllo delle barre rebar espone un set di elementi figli che rappresentano le bande. Ogni banda ha un singolo provider che può esporre proprietà e pattern di controllo. Nell'implementazione di IRawElementProviderSimple::HostRawElementProvider, il provider di banda restituisce il provider di finestre predefinito per la finestra di controllo, che ottiene chiamando UiaHostProviderFromHwnd, passando l'handle della finestra del controllo (HWND). Infine, il provider radice del frammento per la barra degli strumenti implementa l'interfaccia IRawElementProviderHwndOverride e, nella sua implementazione di IRawElementProviderHwndOverride::GetOverrideProviderForHwnd, restituisce il provider di banda appropriato per il controllo contenuto nella finestra specificata.

Disconnessione dei provider

Le applicazioni creano in genere controlli in base alle esigenze e li eliminano in seguito. Dopo aver eliminato un controllo, le risorse del provider di automazione interfaccia utente associate al controllo devono essere rilasciate chiamando il UiaDisconnectProvider.

Analogamente, un'applicazione deve usare la funzione UiaDisconnectAllProviders per rilasciare tutte le risorse di Automazione UI mantenute da tutti i provider nell'applicazione prima della chiusura.

Guida per Programmatori del Provider di Automazione dell'Interfaccia Utente