共用方式為


實作 Server-Side UI自動化提供者

本主題描述如何針對以C++撰寫的自定義控件,實作伺服器端Microsoft UI 自動化提供者。 其中包含下列各節:

如需示範如何實作伺服器端提供者的程式碼範例,請參閱 How-To UI 自動化提供者主題

提供者樹狀結構

您必須針對需要 UIA 用戶端存取的每個 UI 元素實作 UIA 提供者。

例如,每個元素都必須實作 IRawElementProviderFragment,而應用程式的根元素必須實作 IRawElementProviderFragmentRoot。 此外,每個提供者元素都應該連結至:

  • 父母
  • 先前的提供者元素
  • 下一個提供者元素
  • 第一個提供者子系
  • 最後一個提供者子系

提供者介面

下列元件物件模型 (COM) 介面提供自定義控件的功能。 若要提供基本功能,每個UI自動化提供者都必須至少實作 IRawElementProviderSimple 介面。 IRawElementProviderFragmentIRawElementProviderFragmentRoot 介面是選擇性的,但應該針對複雜控件中的元素實作以提供其他功能。

介面 描述
IRawElementProviderSimple 提供窗口裝載之控件的基本功能,包括控制項模式和屬性的支援。
IRawElementProviderFragment 新增複雜控件中元素的功能,包括瀏覽片段、設定焦點,以及傳回元素的邊界矩形。
IRawElementProviderFragmentRoot 新增複雜控制項中根元素的功能,包括尋找位於指定座標的子專案,以及設定整個控件的焦點狀態。

 

注意

在 Managed 程式代碼的 UI 自動化 API 中,這些介面會形成繼承階層。 C++的情況並非如此,其中介面是完全分開的。

 

下列介面提供新增的功能,但實作是選擇性的。

介面 描述
IRawElementProviderAdviseEvents 使提供者可以追蹤事件請求。
IRawElementProviderHwndOverride 可以讓您在片段的UI自動化樹中重新安排視窗為基礎的元素的位置。

 

使用者介面自動化提供者的必要功能

若要與UI自動化通訊,您的控件必須實作下表中所述的主要功能區域。

功能性 實現
將提供者公開以供 UI 自動化使用。 為了回應傳送至控件視窗的 WM_GETOBJECT 訊息,傳回實作 IRawElementProviderSimple的物件。 如果是片段,這必須是片段根目錄的提供者。
提供屬性值。 實作 IRawElementProviderSimple::GetPropertyValue 以提供或覆寫值。
讓客戶端能夠與控件互動。 實作支援每個適當控制元件模式的介面,例如 IInvokeProvider。 請在您的 IRawElementProviderSimple::GetPatternProvider實作中返回這些控制模式提供者。
引發事件。 UiaRaiseAutomationEventIProxyProviderWinEventSink的方法。
啟用在片段中導航和集中注意力。 針對片段內的每個元素實作 IRawElementProviderFragment。 不屬於片段的元素不需要進行處理。
啟用片段中的焦點和尋找子專案。 實現 IRawElementProviderFragmentRoot。 非片段根元素不需要。

 

屬性值

自定義控制元件的UI自動化提供者必須支援UI自動化和用戶端應用程式可以使用的特定屬性。 針對裝載於視窗的專案,UI 自動化可以從預設視窗提供者擷取某些屬性,但必須從自定義提供者取得其他屬性。

一般而言,視窗型控制項的提供者不需要提供由 PROPERTYID所識別的下列屬性:

從視窗取得託管於視窗中的簡單元素或片段根元素的 RuntimeId 屬性。 不過,根目錄下方的片段元素,例如清單框中的清單項目,必須提供自己的標識符。 如需詳細資訊,請參閱 IRawElementProviderFragment::GetRuntimeId

應該針對裝載在 Windows Forms 控件中的提供者傳回 IsKeyboardFocusable 屬性。 在此情況下,默認視窗提供者可能無法擷取正確的值。

Name 屬性通常是由主機提供者提供。

來自供應商的事件

使用者介面自動化提供者應該引發事件,以通知用戶端應用程式 UI 狀態的變更。 下列函式可用來引發事件。

功能 描述
UiaRaiseAutomationEvent 引發各種事件,包括由控件模式觸發的事件。
UiaRaiseAutomationPropertyChangedEvent 當 UI 自動化屬性更改時,會引發一個事件。
UiaRaiseStructureChangedEvent 例如,當 UI 自動化樹狀結構改變時,例如移除或新增元素,會引發事件。

 

事件的目的是通知用戶端在使用者介面中正在發生的某些事情。 提供者應該引發事件,不論變更是由使用者輸入觸發,還是由使用UI自動化的用戶端應用程式觸發。 例如,每當控制項被叫用時,應該引發 UIA_Invoke_InvokedEventId 所識別的事件,無論是透過直接使用者輸入,還是透過用戶端應用程式呼叫 IUIAutomationInvokePattern::Invoke

若要將效能優化,提供者可以選擇性地引發事件,如果沒有註冊任何用戶端應用程式來接收事件,則完全不會引發事件。 下列 API 元素用於優化。

API 元素 描述
Uia客戶正在收聽 此函式會確定任何用戶端應用程式是否已訂閱 UI 自動化事件。
IRawElementProviderAdviseEvents 在片段根上實作此介面可讓提供者在客戶端註冊及取消註冊片段事件的事件處理程式時收到通知。

 

附註

類似於在 COM 程式設計中實作參考計數,使用者介面自動化提供者應該將 IRawElementProviderAdviseEvents::AdviseEventAddedAdviseEventRemoved 方法,視作與 IUnknown::AddRefRelease 方法同樣重要,這些方法都是 IUnknown 介面的一部分。 只要針對特定事件或屬性,AdviseEventAdded 的呼叫次數多於 AdviseEventRemoved,提供者應該繼續引發對應的事件,因為某些用戶端仍持續接收。 或者,使用者介面自動化提供者可以使用 UiaClientsAreListening 函式來判斷是否至少有一個用戶端正在接聽,如果是的話,請引發所有適當的事件。

 

提供者導覽

簡單控制元件的提供者,例如裝載在視窗中的自訂按鈕,不需要支援在 UI 自動化樹狀目錄中導覽。 元素的進出巡覽是由主視窗的預設提供者處理,該提供者是在實作 IRawElementProviderSimple::HostRawElementProvider中指定的。 當您實作複雜自定義控制項的提供者時,您必須支援在片段的根節點與其所有子節點之間,以及在兄弟節點之間進行導航。

注意

根目錄以外的片段元素必須從 HostRawElementProvider傳回 NULL,因為它們不是直接裝載在視窗中,而且沒有預設提供者可以支援巡覽。

 

片段的結構取決於您實作 IRawElementProviderFragment::Navigate。 針對每個片段的每個可能方向,這個方法會傳回該方向中元素的提供者物件。 如果沒有該方向的元素,此方法會傳回 NULL

片段根目錄僅支持流覽至子元素。 例如,當方向 NavigateDirection_FirstChild時,清單框會傳回清單中的第一個專案,並在方向為 NavigateDirection_LastChild時傳回最後一個專案。 片段根目錄不支援瀏覽至父系或同層級,這由主機視窗提供者負責處理。

不是根的片段元素必須支持流覽至父系,以及他們擁有的任何同層級和子系。

指派新父母

彈出窗口實際上是最上層視窗,根據預設,會顯示在使用者介面自動化樹狀結構中作為桌面的子視窗。 不過,在許多情況下,彈出視窗在邏輯上是其他控件的子項。 例如,下拉式方塊的下拉式清單在邏輯上是下拉式方塊的子系。 同樣地,功能表彈出視窗在邏輯上是功能表的子項。 使用者介面自動化提供支援將新的父物件指派給視窗,使其看起來像是相關聯控制項的子物件。

若要為彈出視窗指定一個新的父系:

  1. 建立彈出視窗的提供者。 這需要事先知道彈出窗口的類別。
  2. 按照一般規範實作快顯視窗的所有屬性和控制項模式,彷彿它是獨立的控制項一樣。
  3. 實作 IRawElementProviderSimple::HostRawElementProvider 属性,使其傳回從 UiaHostProviderFromHwnd取得的值,其中 參數是彈出視窗的視窗句柄。
  4. 針對彈出視窗及其父元件實作 IRawElementProviderFragment::Navigate,以便正確處理從邏輯父系到邏輯子系,以及在兄弟姐妹子系之間的導覽。

當 UI 自動化系統遇到彈出視窗時,它會識別到導覽已被從預設模式覆寫,並在彈出視窗作為桌面子視窗出現時將其略過。 相反地,節點只能透過片段到達。

分配新的父級不適用於控制元件可以承載任何類別視窗的情況。 例如,Rebar 控件可以在其頻帶中裝載任何類型的視窗。 為了處理這些案例,UI 自動化支援另一種視窗重新配置形式,如下一節所述。

供應商市場重新定位

UI 自動化片段可能包含兩個以上的元素,每個元素都包含在視窗中。 因為每個視窗都有自己的預設提供者,會將窗口視為包含視窗的子系,因此依預設,UI 自動化樹狀結構會將片段中的視窗顯示為父視窗的子系。 在大部分情況下,這是理想的行為,但有時可能會導致混淆,因為它不符合UI的邏輯結構。

這是一個很好的例子,說明了 Rebar 控件的應用。 Rebar 控制件包含帶狀區域,每個區域都可以包含視窗型控制項,例如工具列、編輯框或下拉式方塊。 Rebar 視窗的預設視窗提供程式將帶狀控制視窗視為子視窗,而 Rebar 提供程式將帶狀視為子功能。 因為視窗提供者與工具條提供者協同合作,並結合其子項,因此帶和視窗型控制項都會顯示為工具條控制項的子項。 不過,在邏輯上,只有帶狀體應該顯示為 Rebar 控制元件的子系,而且每個帶狀體提供者都應該與其所包含控制元件的預設視窗提供者配合。

為了達成此目的,Rebar 控件的片段根提供者會公開一組代表帶狀的子系。 每個頻帶都有單一提供者,可能會公開屬性和控件模式。 在 IRawElementProviderSimple::HostRawElementProvider的實作中,頻帶提供者會傳回控件視窗的默認視窗提供者,其藉由呼叫 UiaHostProviderFromHwnd,傳入控件的視窗句柄 (HWND)。 最後,rebar 的片段根提供者會實作 IRawElementProviderHwndOverride 介面,並在其實作 IRawElementProviderHwndOverride::GetOverrideProviderForHwnd中,傳回指定視窗中所含控件的適當頻帶提供者。

解除與提供商的連線

應用程式通常會視需要建立控件,並在之後加以終結。 終結控件之後,應該藉由呼叫 UiaDisconnectProvider來釋放與控件相關聯的 UI 自動化提供者資源。

同樣地,應用程式應該使用 UiaDisconnectAllProviders 函式,在關閉之前釋放應用程式中所有提供者所持有的所有 UI 自動化資源。

UI 自動化提供者程式設計人員指南