|Important||This document may not represent best practices for current development, links to downloads and other resources may no longer be valid. Current recommended version can be found here.|
Server-Side UI Automation Provider Implementation
This section describes how to implement a server-side UI Automation provider for a custom control.
The implementation for Windows Presentation Foundation (WPF) elements and non-WPF elements (such as those designed for Windows Forms) is fundamentally different. WPF elements provide support for UI Automation through a class derived from AutomationPeer. Non-WPF elements provide support through implementations of provider interfaces.
This topic contains the following sections.
Providers should be written so that they can work in a partial-trust environment. Because UIAutomationClient.dll is not configured to run under partial trust, your provider code should not reference that assembly. If it does so, the code may run in a full-trust environment but then fail in a partial-trust environment.
In particular, do not use fields from classes in UIAutomationClient.dll such as those in AutomationElement. Instead, use the equivalent fields from classes in UIAutomationTypes.dll, such as AutomationElementIdentifiers.
For more information on this topic, please see UI Automation of a WPF Custom Control.
Custom controls that are not part of the WPF framework, but that are written in managed code (most often these are Windows Forms controls), provide support for UI Automation by implementing interfaces. Every element must implement at least one of the interfaces listed in the first table in the next section. In addition, if the element supports one or more control patterns, it must implement the appropriate interface for each control pattern.
Your UI Automation provider project must reference the following assemblies:
This section contains the following subsections.
- Provider Interfaces
- Requirements for Non-WPF Providers
- Property Values in Non-WPF Providers
- Events in Non-WPF Providers
- Non-WPF Provider Navigation
- Non-WPF Provider Reparenting
- Non-WPF Provider Repositioning
Every UI Automation provider must implement one of the following interfaces.
Provides functionality for a simple control hosted in a window, including support for control patterns and properties.
Inherits from IRawElementProviderSimple. Adds functionality for an element in a complex control, including navigation within the fragment, setting focus, and returning the bounding rectangle of the element.
Inherits from IRawElementProviderFragment. Adds functionality for the root element in a complex control, including locating a child element at specified coordinates and setting the focus state for the entire control.
The following interfaces provide added functionality but are not required to be implemented.
Enables the provider to track requests for events.
Enables repositioning of window-based elements within the UI Automation tree of a fragment.
All other interfaces in the System.Windows.Automation.Provider namespace are for control pattern support.
Requirements for Non-WPF Providers
In order to communicate with UI Automation, your control must implement the following main areas of functionality:
Expose the provider to UI Automation
In response to a WM_GETOBJECT message sent to the control window, return the object that implements IRawElementProviderSimple (or a derived interface). For fragments, this must be the provider for the fragment root.
Provide property values
Implement GetPropertyValue to provide or override values.
Enable the client to interact with the control
Call one of the static methods of AutomationInteropProvider to raise an event that a client can listen for.
Enable navigation and focusing within a fragment
Implement IRawElementProviderFragment for each element within the fragment. (Not necessary for elements that are not part of a fragment.)
Enable focusing and location of child element in a fragment
Implement IRawElementProviderFragmentRoot. (Not necessary for elements that are not fragment roots.)
Property Values in Non-WPF Providers
UI Automation providers for custom controls must support certain properties that can be used by the automation system as well as by client applications. For elements that are hosted in windows (HWNDs), UI Automation can retrieve some properties from the default window provider, but must obtain others from the custom provider.
Providers for HWND based controls do not usually need to provide the following properties (identified by field values):
The RuntimeIdProperty of a simple element or fragment root hosted in a window is obtained from the window; however, fragment elements below the root (such as list items in a list box) must provide their own identifiers. For more information, see GetRuntimeId.
The IsKeyboardFocusableProperty should be returned for providers hosted in a Windows Forms control. In this case, the default window provider may be unable to retrieve the correct value.
For example code, see Return Properties from a UI Automation Provider.
Events in Non-WPF Providers
UI Automation providers should raise events to notify client applications of changes in the state of the UI. The following methods are used to raise events.
Raises various events, including events triggered by control patterns.
Raises an event when a UI Automation property has changed.
Raises an event when the structure of the UI Automation tree has changed; for example, by the removal or addition of an element.
The purpose of an event is to notify the client of something taking place in the user interface (UI), whether or not the activity is triggered by the UI Automation system itself. For example, the event identified by InvokedEvent should be raised whenever the control is invoked, either through direct user input or by the client application calling Invoke.
To optimize performance, a provider can selectively raise events, or raise no events at all if no client application is registered to receive them. The following methods are used for optimization.
This static property specifies whether any client applications have subscribed to UI Automation events.
The provider's implementation of this interface on a fragment root enables it to be advised when clients register and unregister event handlers for events on the fragment.
Non-WPF Provider Navigation
Providers for simple controls such as a custom button hosted in a window (HWND) do not need to support navigation within the UI Automation tree. Navigation to and from the element is handled by the default provider for the host window, which is specified in the implementation of HostRawElementProvider. When you implement a provider for a complex custom control, however, you must support navigation between the root node of the fragment and its descendants, and between sibling nodes.
Elements of a fragment other than the root must return a null reference from HostRawElementProvider, because they are not directly hosted in a window, and no default provider can support navigation to and from them.
The structure of the fragment is determined by your implementation of Navigate. For each possible direction from each fragment, this method returns the provider object for the element in that direction. If there is no element in that direction, the method returns a null reference.
The fragment root supports navigation only to child elements. For example, a list box returns the first item in the list when the direction is FirstChild, and the last item when the direction is LastChild. The fragment root does not support navigation to a parent or siblings; this is handled by the host window provider.
Elements of a fragment that are not the root must support navigation to the parent, and to any siblings and children they have.
Non-WPF Provider Reparenting
Pop-up windows are actually top-level windows, and so by default appear in the UI Automation tree as children of the desktop. In many cases, however, pop-up windows are logically children of some other control. For example, the drop-down list of a combo box is logically a child of the combo box. Similarly, a menu pop-up window is logically a child of the menu. UI Automation provides support to reparent pop-up windows so that they appear to be children of the associated control.
To reparent a pop-up window:
Create a provider for the pop-up window. This requires that the class of the pop-up window is known in advance.
Implement all properties and patterns as usual for that pop-up, as though it were a control in its own right.
Implement Navigate for the pop-up window and its parent so that navigation is handled properly from the logical parent to the logical children, and between sibling children.
When UI Automation encounters the pop-up window, it recognizes that navigation is being overridden from the default, and skips over the pop-up window when it is encountered as a child of the desktop. Instead, the node will only be reachable through the fragment.
Reparenting is not suitable for cases where a control can host a window of any class. For example, a rebar can host any type of HWND in its bands. To handle these cases, UI Automation supports an alternative form of HWND relocation, as described in the next section.
Non-WPF Provider Repositioning
UI Automation fragments may contain two or more elements that are each contained in a window (HWND). Because each HWND has its own default provider that considers the HWND to be a child of a containing HWND, the UI Automation tree will, by default, show the HWNDs in the fragment as children of the parent window. In most cases this is desirable behavior, but sometimes it can lead to confusion because it does not match the logical structure of the UI.
A good example of this is a rebar control. A rebar contains bands, each of which can in turn contain an HWND-based control such as a toolbar, an edit box, or a combo box. The default window provider for the rebar HWND sees the band control HWNDs as children, and the rebar provider sees the bands as children. Because the HWND provider and the rebar provider are working in tandem and combining their children, both the bands and the HWND-based controls appear as children of the rebar. Logically, however, only the bands should appear as children of the rebar, and each band provider should be coupled with the default HWND provider for the control it contains.
To accomplish this, the fragment root provider for the rebar exposes a set of children representing the bands. Each band has a single provider that may expose properties and patterns. In its implementation of HostRawElementProvider, the band provider returns the default window provider for the control HWND, which it obtains by calling HostProviderFromHandle, passing in the control's window handle. Finally, the fragment root provider for the rebar implements the IRawElementProviderHwndOverride interface, and in its implementation of GetOverrideProviderForHwnd it returns the appropriate band provider for the control contained in the specified HWND.