Implementare un provider di Server-Side Automazione interfaccia utente

Questo argomento descrive come implementare un provider microsoft Automazione interfaccia utente 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 Procedure per i provider di Automazione interfaccia utente.

Struttura dell'albero del provider

È 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 collegarsi a un oggetto:

  • padre
  • elemento del provider precedente
  • elemento provider successivo
  • primo provider figlio
  • ultimo provider figlio

Interfacce del provider

Le interfacce COM (Component Object Model) seguenti forniscono funzionalità per i controlli personalizzati. Per fornire funzionalità di base, ogni provider di Automazione interfaccia utente 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 modelli di controllo e le proprietà.
IRawElementProviderFragment Aggiunge funzionalità per un elemento in un controllo complesso, incluso lo spostamento nel frammento, l'impostazione dello stato attivo e la restituzione del rettangolo di selezione dell'elemento.
IRawElementProviderFragmentRoot Aggiunge la funzionalità per l'elemento radice in un controllo complesso, inclusi l'individuazione di un elemento figlio in corrispondenza delle coordinate specificate e l'impostazione dello stato attivo per l'intero controllo.

 

Nota

Nell'API Automazione interfaccia utente per il codice gestito, queste interfacce formano una gerarchia di ereditarietà. Questo non è il caso in C++, dove 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 per eventi.
IRawElementProviderHwndOverride Consente di riposizionare gli elementi basati su finestra nell'albero 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 aree principali della funzionalità descritte nella tabella seguente.

Funzionalità Implementazione
Esporre il provider a 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, deve trattarsi del 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 modello di controllo appropriato, ad esempio IInvokeProvider. Restituire questi provider di modelli 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 di elementi figlio in un frammento. Implementare IRawElementProviderFragmentRoot. Non necessario per gli elementi che non sono radici frammento.

 

Valori della proprietà

Automazione interfaccia utente provider per i controlli personalizzati devono supportare determinate proprietà che possono essere usate da 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 un elemento semplice o radice di frammento 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 specificare 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 finestra predefinito potrebbe non essere in grado di recuperare il valore corretto.

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

Eventi dai provider

Automazione interfaccia utente provider devono generare eventi per notificare alle applicazioni client modifiche nello stato dell'interfaccia utente. Le funzioni seguenti vengono usate per generare eventi.

Funzione Descrizione
UiaRaiseAutomationEvent Genera vari eventi, inclusi gli eventi attivati dai pattern di controllo.
UiaRaiseAutomationPropertyChangedEvent Genera un evento quando una proprietà Automazione interfaccia utente è stata modificata.
UiaRaiseStructureChangedEvent Genera un evento quando la struttura dell'albero 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 viene richiamato il controllo, tramite input utente diretto o dall'applicazione client che chiama IUIAutomationInvokePattern::Invoke.

Per ottimizzare le prestazioni, un provider può generare eventi in modo selettivo o non generarne 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 Automazione interfaccia utente eventi.
IRawElementProviderAdviseEvents L'implementazione di questa interfaccia in una radice di frammento consente al provider di essere avvisati quando i client registrano e annullano la registrazione dei gestori 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 trattino i metodi IRawElementProviderAdviseEventAdded e AdviseEventRemoved come i metodi IUnknown::AddRef e Release dell'interfaccia IUnknown. Finché AdviseEventAdded è stato chiamato più volte rispetto a AdviseEventRemoved per un evento o una proprietà specifica, il provider deve continuare a generare eventi corrispondenti, perché alcuni client sono ancora in ascolto. In alternativa, Automazione interfaccia utente provider possono usare la funzione UiaClientsAreListening per determinare se almeno un client è in ascolto e, in tal caso, generare tutti gli eventi appropriati.

 

Navigazione provider

I provider per controlli semplici, ad esempio un pulsante personalizzato ospitato in una finestra, non devono supportare lo spostamento nell'albero 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 la navigazione tra il nodo radice del frammento e i relativi discendenti, nonché 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 lo spostamento da e verso di essi.

 

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

La radice del frammento supporta la navigazione solo verso gli elementi figlio. Ad esempio, una casella di riepilogo restituisce il primo elemento dell'elenco quando la direzione è NavigateDirection_FirstChild e 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; gestito dal provider della finestra host.

Gli elementi di un frammento non radice devono supportare la navigazione verso l'elemento padre e verso eventuali elementi di pari livello e figli.

Assegnazione di un nuovo elemento padre

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

Per assegnare un nuovo elemento padre 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 a proprio diritto.
  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 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 lo spostamento viene sottoposto a override dall'impostazione predefinita e ignora la finestra popup quando viene rilevata come elemento 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

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

Un esempio valido è 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 della barra di rebar vede le finestre di controllo banda come elementi figlio e il provider rebar vede le bande come elementi figlio. Poiché il provider di finestre e il provider rebar lavorano in tandem e combinano i relativi elementi figlio, sia le bande che i controlli basati su finestra vengono visualizzati come elementi figlio del controllo rebar. Logicamente, tuttavia, solo le bande dovrebbero apparire come elementi figlio del controllo rebar e ogni provider di banda deve essere associato al provider di finestre predefinito per il controllo che contiene.

A tale scopo, il provider radice di frammenti per il controllo rebar espone un set di elementi figlio 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 band 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 di rebar implementa l'interfaccia IRawElementProviderHwndOverride e nell'implementazione di IRawElementProviderHwndOverride::GetOverrideProviderForHwnd, restituisce il provider di banda appropriato per il controllo contenuto nella finestra specificata.

Disconnessione dei provider

Le applicazioni in genere creano controlli quando sono necessari 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 UiaDisconnectProvider.

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

Guida per programmatori di provider Automazione interfaccia utente