Skip to main content

Windows Dev Center

Windows (Win32) Accessibility for Developers

Overview

Welcome to the Win32 for Developers course. As a developer, you are responsible for implementing accessible solutions for end users, and a little work up front on your part goes a long way towards integrated accessible code in the final product. This course is tailored for Win32 developers and will give you specific tools and skills to create more accessible, user-friendly products.

As a result of this course, you will be able to:

  • Explain how Microsoft Active Accessibility (MSAA) and UI Automation (UIA) work in Win32 to provide programmatic access for users of Assistive Technology (AT)
  • Determine how to approach accessibility when developing customized controls

Welcome

Win32 has been around since 1985 and is widely used in many programs around the world, which means creating accessible products in Win32 is essential to the continued success of a wide range of users. This course assumes you have a working knowledge of Win32 common controls, and how to customize them. As a developer working with Win32, you face unique challenges when developing for accessibility.

This course is organized to help you locate information quickly when you need it, and is laid out in the following manner:

  • Programmatic Access
  • Common Controls
  • Keyboard Navigation
  • System Settings
  • Custom Controls

Programmatic Access

In order to create accessible products, you need to be aware of the accessibility-related Application Programming Interfaces (APIs) used for Win32 product development. Programmatic exposure for Win32 has historically been provided through Microsoft Active Accessibility (MSAA), but newer implementations are now using UI Automation (UIA). Understanding these two accessibility APIs will assist you in making the best choice for implementing programmatic access, which ensures that all UI elements are programmatically exposed to assistive technology (AT).

In the Windows operating system, there are two APIs that provide programmatic access for Win32 controls:

  • UI Automation (UIA): An accessibility API supported natively by newer controls that can also take advantage of MSAA implementations by “bridging,” or cross-communicating from UIA to MSAA. Use when implementing new controls, complex controls, or when MSAA is poorly implemented in the current code.
  • Microsoft Active Accessibility (MSAA): An older accessibility API based on implementations of the IAccessible Component Object Model (COM) Interface.

If you need to modify the default accessibility properties of a control, you can use two different techniques:

  • Dynamic Annotation: A technique to modify UIA or MSAA properties.
  • IAccessibleEx: A way to expose UIA patterns and properties to controls that only have an MSAA solution.

Use the table below to determine the best approach for implementing your control(s).

If You Are Implementing…The Suggested Approach Is…
A new controlImplement UIA
Common controls with no modification Follow the techniques described in the Common Controls section of this course
Common controls with minor modificationUse Dynamic Annotation or IAccessibleEx

Common Controls

In most cases, a common control implemented correctly will provide all the necessary default programmatic access without additional work on your part. For example, when implementing a “button” common control the MSAA information, such as the role, will automatically be provided.

If you use common controls that do not have the name provided on the control (such as button) or lack a label, it is possible to insert a hidden text label preceding the control in the .rc file with a proper label. This will allow the accessibility frameworks to correctly set the name without you customizing it.

Common Mistakes: The most common mistake made in providing programmatic access to W32 controls is not specifying the controls in the correct order. When using common controls, the .rc file should always have the controls listed in the logical order in which they appear on the UI. For example, text labels should always precede the controls they label in the .rc file. This allows MSAA or UI Automation to create the tree correctly and set the elements' names correctly.

Test Your Control: You must test your implementation to ensure the programmatic access is provided correctly. If information is missing or incorrect, check your control's implementation, and use dynamic annotation, if needed, to override basic properties both in MSAA and UI Automation (see the Dynamic Annotation section of this course for more information).

Modifying Common Controls

When using common controls for your application, the default programmatic access for correctly coded controls is usually adequate.

You will need to modify the properties for the control in cases where you:

  • Are unable to provide a text label (visible or invisible).
  • Subclass Win32 controls to change their behavior.
  • Use the controls in ways not described by MSDN.

It is possible to change the MSAA properties of the controls but this should only be done if you are able to describe the functionality of the control with MSAA. Otherwise you will have to use a UIA solution.

Before you are able to make the necessary changes, you need to understand if MSAA can describe your controls completely; you need to understand the MSAA architecture. This section will give you an overview of the MSAA architecture and introduce you to the concepts of Dynamic Annotation and IAccessibleEx, both of which can be used to describe your controls and their functionality.

MSAA Overview

The IAccessible interface is the heart of MSAA. Applications implement this COM interface to represent their custom user interface elements, which can include their client area as accessible objects, if necessary. Client applications call IAccessible methods and properties to obtain information about an application's user interface and data.

Providing programmatic access using MSAA requires that you address the following issues:

  • Describe the control through MSAA properties (discussed in this course)
  • Provide an accurate MSAA hierarchy tree
  • Ensure the correct WinEvents are fired

Include initguid.h: As with any COM interface, the system file initguid.h should be included in any source code that requires MSAA.

Bridge/Proxy Communication: What happens when an application implements UI Automation and tries to communicate with an AT that uses MSAA, or vice versa?

What happens when an application implements UI Automation and tries to communicate with an AT that uses MSAA, or vice versa? UIA has a built-in technique to connect clients (AT applications, such as a screen reader application) with your application, regardless of which API is implemented.

The UI Automation to MSAA Bridge (MSAA Bridge) enables client applications that use MSAA to access applications that implement UIA. By bridging MSAA and UIA together, MSAA-based clients, such as screen reader on Windows XP, can programmatically interact with UIA-based providers of UI Automation elements, patterns, properties and events to MSAA accessible objects and properties. It is part of the UIA (Proxy) to treat the enhanced accessible MSAA object as an automation element of the UIA object.

UIA providers can supply information to MSAA clients, and MSAA servers can provide information to UIA client applications. However, because MSAA does not expose as much information as UIA, the two models are not fully compatible. Custom controls built using an MSAA server do not make use of the Proxy, and require additional implementation for cross-communication and correct translation for the navigation tree between APIs.

Further information can be downloaded from Proxy and the MSAA Bridge.

Dynamic Annotation vs. IAccessibleEx

If your current common control has properties which need to be adjusted, Dynamic Annotation or IAccessibleEx may be good choices for you. New controls that will need additional functionality, or the flexibility to change functionality in the future, should implement UIA.

How do you determine whether IAccessibleEx or a type of Dynamic Annotation is the correct option for your control? The answer is based on what properties need to be changed or added. The table below lays out what each option will do, and when to use it.

Accessibility ImplementationWhat Does It Do?When Do I Use It?
Dynamic Annotation – Direct Annotation

Fix MSAA or UI Automation properties in an MSAA-based Common Control.

You may need to add support for WinEvent for changes made.

You are using a platform that already implements an accessibility API and you are changing the control that is exposed. No structural or fundamental issue can be fixed by Dynamic Annotation.
Dynamic Annotation – Value Map Annotation

Fixes MSAA value of Win32 Slider (trackbar32) or state property of items in Win32 SysListView32 / SysTreeView32 controls.

You may need to add support for WinEvent for changes made.

When a List-View or Tree-View incorporates the use of images, and you want to provide a custom accessible description (Description property) based on that image (for owner-drawn controls).

OR

When a List-View or Tree-View control incorporates the use of images to make the tree or list items act like simple controls, typically checkboxes or radio buttons (for owner-drawn controls).

OR

When a slider uses "select one-from-many" discrete values.

IAccessibleExExpose UIA control patterns and properties from MSAA controls.The control has a native MSAA solution, but it is not fully describing the control. When this is used, the base MSAA implementations must be fully implemented. The baseline MSAA structure must be already per to resulting UIA structure; no structural arrange can be made after baseline MSAA structure.

What would be the best option for each scenario? Review these scenarios to learn more about how to select which option works best for your situation.

Scenario 1

A custom control has some IAccessible information implemented, but also needs to report custom properties such as the range or step values of the progress bar.

Which is the best option to handle this situation?

Answer: IAccessibleEx-- IAccessibleEx is the best option when you seek to expose complex UIA or MSAA properties from a control.

Scenario 2

An edit box is missing its Name, and there is no ability to place a label (either visible or invisible) preceding the control.

Which is the best option to handle this situation?

Answer: Dynamic Annotation (Direct Annotation)--Direct Annotation is useful when the Name property needs to be corrected or changed.

Scenario 3

You have an existing MSAA slider control that you want to make into a slider that programmatically exposes resolution information as you move the slider up and down.

Which is the best option to handle this situation?

Answer: Dynamic Annotation (Value Map Annotation)--Value Map Annotation is used to correct and change the properties exposed by MSAA slider controls.

Dynamic Annotation

Dynamic Annotation is a technology introduced with MSAA version 2.0. Its purpose is to enable a customized common control to expose accessibility information without the need to fully implement the IAccessibleinterface. Dynamic Annotation works by overriding the default properties that are created for standard Windows controls. If you are changing a control, you can use Dynamic Annotation to modify MSAA, or add some UIA properties. Four examples of MSAA properties you may need to modify are: Name, Role, State, and Value.

Name & Role

MSAA Name

The Name for elements in an application is assigned in the code by the developer. Many objects such as icons, menus, check boxes, combo boxes, and other controls have labels that are displayed to users. Any label that is displayed to users on or before a control, such as a button, should be the default for the object's name property. Ensure that the Name of the object makes sense to a user, describes the control properly, and does not duplicate the name of a nearby control. For example, if a button is called “Button” and the button next to it is also called “Button,” a user of AT will not be able to identify the function of either button, nor understand the differences between them. A better Name for them might be “Previous page” and “Next page.”

Label as a Default Name: When using common controls, the text label that precedes the control needs to be specified directly before the control in the “.rc” file. Without this, the label will not default as the Name property for the object.

Name Localization: When you annotate the Name, the string should be localized. This allows users to receive information in their native language when using your product.

MSAA Role

The Role information is based on the type of control you choose. Select the correct Role to implement based on the desired function of the control. For example the classic confirmation push button “OK” in most dialogues would have an MSAA role of ROLE_SYSTEM_PUSHBUTTON.

A list of windows controls and their functions can be found on MSDN.

Choose the Correct Role: Choosing the incorrect Role for your control may result in impaired or completely inaccessible control functionality. When you change the MSAA role, many of the other properties must be changed as well. For example, do not annotate a button as a slider because the baseline Win32 button control does not offer all of the functionality of a slider control. A slider has “values” and by only changing the Role to “slider” you will not expose your control correctly.

State & Value

MSAA State

The State property describes an object's status at a moment in time. MSAA provides object state constants, defined in oleacc.h, that are combined to identify an object's state. It is required that server developers use these predefined state values. If predefined state values are returned, clients use GetStateText to retrieve a localized string that describes the state. All objects support the State property. Implementation for a button can include the following constants:

  • STATE_SYSTEM_INVISIBLE (if invisible or not shown yet)
  • STATE_SYSTEM_UNAVAILABLE (if disabled)
  • STATE_SYSTEM_FOCUSED (if it has focus)
  • STATE_SYSTEM_FOCUSABLE (if the user can tab to it)
  • STATE_SYSTEM_PRESSED (if button is depressed)
  • STATE_SYSTEM_DEFAULT (if it is a default button on a dialog box)

Occasionally Animated Graphics: Graphics that are occasionally animated should have the State property set to STATE_SYSTEM_ANIMATED and the Role property set to ROLE_SYSTEM_GRAPHIC.

MSAA Value

Value is returned for objects where percentages, integers and non-integers, textual or visual information are contained in the object. For example, numeric values returned from scroll bar and track bar accessible objects indicate percentages. In this case, the Value returned is an integer between zero and one hundred. Objects that don’t use percentages might use a limited range, such as between one and sixteen. Some objects return strings that correspond to settings. For example, screen size or Internet security levels might be mapped to the Value in place of an integer or percentage. Another example is an editable text field. The Value for the text field is the text typed into and contained by the field. A push button, however, does not have a value.

Value: Not all objects have a Value assigned to them.

Going Further

There are other MSAA properties that should be implemented depending on your control’s role and functionality. You can find out more about other MSAA properties and their implementations on MSDN.

Using Dynamic Annotation

Dynamic Annotation provides two different mechanisms for handling annotations:

Sample code for Dynamic Annotation of a button taken from MSDN

HRESULT CFancyTextControl::SetAccessibleProperties()







{







    // We will assume COM has been initialized.







    IAccPropServices* pAccPropServices = NULL;







    HRESULT hr = CoCreateInstance(CLSID_AccPropServices,







        NULL,







        CLSCTX_SERVER,







        IID_IAccPropServices,







       (void**)&pAccPropServices);







    if (SUCCEEDED(hr))







    {







        VARIANT var;







        var.vt = VT_I4;







        var.lVal = ROLE_SYSTEM_STATICTEXT;







        hr = pAccPropServices->SetHwndProp(_hwnd,







            OBJID_CLIENT,







            CHILDID_SELF,







            PROPID_ACC_ROLE,







            var);







        if (SUCCEEDED(hr))







        {







            var.lVal = STATE_SYSTEM_READONLY;







            hr = pAccPropServices->SetHwndProp(_hwnd,







                OBJID_CLIENT,







                CHILDID_SELF,







                PROPID_ACC_STATE,







                var);







        }







        pAccPropServices->Release();







    }







    return hr;







}

Affecting Expected Behavior: Dynamic Annotation can affect the expected behavior of a control. For example, if you annotate the Role from “pushbutton” to “entry” (creates editable text) it may cause the AT to interpret the control as an unreadable control.

Adding Event Support: Dynamic Annotation does not provide event support. Developers must add event support manually.

Community Promise Dynamic Annotation Section: The properties supported by dynamic annotations are present in the Common Infrastructure section of the Community Promise.

Direct Annotation

Direct Annotation is used to override default values for properties such as the string for a Name. It can also be used to add UI Automation properties, including the Automation ID (see the Automation ID section in this course for more information).

More information about Direct Annotation is available on MSDN.

Value Map Annotation

Value Map Annotation is used to specify strings for slider values and specify roles, states, and descriptions for icons in list and tree views for a control. This type of annotation is only supported for the limited controls that require a Value, such as checkbox or sliders. With value map annotation, you can use a mapping string to indicate how the image index of an item in a list view or tree view corresponds to its role or state. For example, a mapping string may indicate that a list view's image index 0 maps to a role of check box, while image index 1 maps to a role of radio button.

Another example is the Internet Explorer “Security” tab in the Internet Options window, which has a slider on it for level of security on the local intranet. Sliders use numbers by default, but a security of “25” or “63” doesn’t give the user very much information about their level of allowed security. Value Map Annotation can map values to a string, such as “25” being equal to “Medium-low” in order to make the control more comprehensible to a user.

More information about Value Map Annotation is available on MSDN.

IAccessibleEx

The IAccessibleEx interface enables legacy MSAA applications to add support for specific UI Automation patterns and properties without creating a native UI Automation solution. With IAccessibleEx, you can implement richer information about your control using the UIA control patterns and properties.

When to Use IAccessibleEx

IAccessibleEx is a bridging technique that allows some UIA information to be implemented. On the whole, however, any new application or control could be more effective when created using UIA, which provides the best flexibility to support new UI behaviors for accessibility.

While IAccessibleEx can be a cost effective way of supporting UIA, your application should meet the following two criteria if you are to consider using it:

  • The control must appear in the MSAA tree correctly. IAccessibleEx cannot fix problems with existing accessible object hierarchies.
  • Your IAccessibleEx implementation must be compliant with both MSAA and UIA specifications. Tools such as UISpy and AccChecker are available to validate compliance with both specifications.

If either of these baseline requirements is not met, you should implement UIA natively instead of IAccessibleEx. You may choose to keep legacy MSAA implementations for backward compatibility, if necessary.

Implement IAccessibleEx

Should IAccessibleEx be the better choice for your situation, follow these steps to implement it:

1. Implement IAccessibleEx. Here is an implementation of the GetObjectForChild and GetIAccessiblePair methods so UI Automation can map an IAccessible and ChildId pair to a corresponding IAccessibleEx instance.

HRESULT CListboxAccessibleObject::GetObjectForChild(long idChild, IAccessibleEx **pRetVal)







{















VARIANT vChild;







vChild.vt = VT_I4;







vChild.lVal = idChild;







HRESULT hr = ValidateChildId(vChild);







if (FAILED(hr))







{















return E_INVALIDARG;















}







// List item accessible objects are stored as an array of







// pointers; for the purpose of this example, I assumed 







// the list contents will not change. Accessible objects 







// are created only when needed.







if (itemProviders[idChild - 1] == NULL)







{















// Create an object that supports UI Automation and







// IAccessibleEx for the item.







itemProviders[idChild - 1] = new CListItemAccessibleObject(idChild, g_pListboxControl);







if (itemProviders[idChild - 1] == NULL)







{















return E_OUTOFMEMORY;















}















}







IAccessibleEx* pAccEx = static_cast<IAccessibleEx*>(itemProviders[idChild - 1]);







if (pAccEx != NULL)







{















pAccEx->AddRef();















}







*pRetVal = pAccEx;







return S_OK;















}







HRESULT CListItemAccessibleObject::GetIAccessiblePair(IAccessible **ppAcc, long *pidChild)







{















if (!ppAcc || !pidChild)







{















return E_INVALIDARG;















}







CListboxAccessibleObject* pParent = m_control->GetAccessibleObject();







HRESULT hr = QueryInterface(__uuidof(IAccessible), (void**)ppAcc);







if (FAILED(hr))







{















*pidChild = 0;







return E_NOINTERFACE;















}







*pidChild = m_childID;







return S_OK; 















}

2. Expose IAccessibleEx with IServiceProvider::QueryService.

3. Implement IRawElementProviderSimple to provide property values and pattern providers for your control.

4. Implement control patterns and properties that describe your control.

5. Implement WinEvents for your control.

For more information on IAccessibleEx, see the Microsoft Community Promise.

Keyboard Navigation

You must plan for effective keyboard interactions in your product. Think through the navigation for the product, which items should receive focus, and how non-text items will be labeled in order to provide the best experience for users. All applications must be navigable using only a keyboard. Tab order and shortcut keys should all be provided in the spec. Further keyboard navigation information can be found on MSDN.

High DPI

Writing a DPI-aware application is the key to making a UI look consistently good across a wide variety of high DPI display settings. An application that is not DPI-aware but is running on a high DPI display setting can suffer from many visual artifacts, including incorrect scaling of UI elements, clipped text, and blurry images. By adding support in your application for DPI-awareness, your application’s UI is more predictable, making it more visually appealing to users.

As more manufacturers ship greater numbers of high-resolution displays, the default 96 DPI setting can no longer be assumed by applications.

While the Win32 API provides a function declaring an application as dpi-aware, its use is discouraged, except in very specific circumstances. In general, using an application manifest is the recommended process for declaring an application to be dpi-aware.

High Contrast

Supporting high contrast is an important part of making your application accessible. A high contrast mode must remove flashing animations, and remove or reduce transition animations. It should also be set to omit non-functional images, gradients or patterns behind text. When creating applications, be sure to adhere to the high contrast standards on MSDN. Be sure not to hard-code colors and have alternative color icons available for high-contrast setting users. Further information on high contrast in Win32 is available on the High Contrast Parameter page at MSDN.

Other Considerations

There are two additional accessibility issues that you will want to think through when developing your product. These are color and sound.

Color

Text and background contrasts need to meet at least a 5:1 color contrast ratio. In addition, keep color choices appropriate for users with colorblindness. Be sure to provide visual cues in formats which rely solely on color to convey changes or information in the user interface (UI). Different icons or use of words will help colorblind users get the information they need.

Sound

Provide closed captioning or visual notifications for sounds used in applications. Users with hearing impairments or computers that do not have sound available will need alternate notifications such as these.

Win32 Accessibility Parameters: For more information about Win32 accessibility parameters, visit the Accessibility Parameterspage on MSDN.

Custom Controls

Using common controls can sometimes provide insufficient flexibility for end-user scenarios when developing Win32 applications. In these cases, you may choose to create custom Win32 controls for your application. This decision, however, can be very expensive depending on the complexity of the control. For example, when creating a custom interface with rich functionality, you must manually code to provide programmatic access for each unique control in that custom implementation, as well as create the logical hierarchy and raise events for clients.

This section will provide you with information about creating custom controls using UIA. Important topics covered for custom controls include:

  • Understanding the UIA architecture
  • Designing the custom control
  • Implementing a custom control using UIA

Understanding the UIA Architecture

UIA enables programmatic access to AT through six components:

  • Automation Elements: Individual components in the UI that represent objects, often more granular than an hWnd
  • Automation Properties: Provide specific information about automation elements
  • UIA Tree: Facilitates navigation through the structure of the UI
  • Control Patterns: Define a particular aspect of a control's functionality, consisting of information about properties, methods, events, and structures
  • Automation Events: Provide event notifications and information
  • UIA Control Types: An automation element property that specifies a well-known control that the element represents, such as: Button, Check Box, Combo Box, Data Grid, Document, Hyperlink, Image, ToolTip, Tree, and Window

Exploring UIA Components

The UIA Tree

The UIA Tree represents the entire UI. The root element is always the desktop, and the child elements (called fragments) are application windows. The UIA Tree maps to all the elements on the desktop.

The UIA Tree is not a fixed structure and is seldom seen in its totality because it might contain thousands of elements. Parts of it are built as they are needed, and it can undergo changes as elements are added, moved, or removed. To help AT clients process UI information more effectively, the framework supports alternative views of the UIA Tree which may be used by some applications:

  • Raw View: The full tree of AutomationElement objects for which the desktop is the root. The raw view closely follows the native programmatic structure of an application. Because this view depends on the underlying UI framework, the raw view of a WPF button will have a different raw view from that of a Win32 button. The raw view is obtained by searching for elements without specifying properties or by using the RawViewWalker to navigate the tree.
  • Control View: A subset of the raw view which closely maps to the UI structure perceived by an end user. It includes all UI items from the raw view that an end user would understand as interactive or contributing to the logical structure of the control in the UI.

Examples of UI items that contribute to the logical structure of the UI, but are not interactive themselves, are item containers such as list view headers, toolbars, menus, and the status bar.

Non-interactive items that will be seen in the control view are graphics with information and static text in a dialog box. Non-interactive items that are included in the control view cannot receive keyboard focus. Non-interactive items used simply for layout or decorative purposes will not be seen in the control view. An example is a panel that was used only to lay out the controls in a dialog box but does not itself contain any information.

The control view is obtained by searching for elements that have the IsControlElement property set to true, or by using the ControlViewWalker to navigate the tree.

  • Content View: A subset of the control view which contains UI items that convey the true information in a user interface. This includes UI items that can receive keyboard focus and some text that is not a label on a UI item.

For example, the values in a drop-down combo box will appear in the content view because they represent the information being used by an end user. In the content view, a combo box and list box are both represented as a collection of UI items where one, or perhaps more than one, item can be selected. That one is always open and one can expand and collapse is irrelevant in the content view because it is designed to show the data, or content, that is being presented to the user.
The content view is obtained by searching for elements that have the IsContentElement property set to true, or by using the ContentViewWalker to navigate the tree.

Automation Element:

UIA exposes every piece of the UI to client applications as an Automation Element. Providers supply property values for each element. Elements are exposed as a tree structure, with the desktop as the root element.

Automation elements sometimes implement methods that allow AT to interact with the element. When the user invokes a method on an element, it automatically invokes the corresponding method on the provider.

Automation Element Properties:

UIA providers expose properties of UI Automation elements. These properties enable UIA client applications to discover information about pieces of the user interface (UI), especially controls, including both static and dynamic data.

The UIA specification defines two kinds of automation properties:

1. Automation Element Properties: apply to all elements, provide fundamental information about the element such as its name.

2. Control Pattern Properties: apply to control patterns, such as ScrollPattern properties that enable a client application to discover whether a window is vertically or horizontally scrollable.

Control Patterns:

Control patterns are collections of associated properties, events, and methods that describe an element. There are 22 control patterns defined to date, and more than one pattern can apply to a single element. Control patterns also expose methods that enable clients to get further information about the element and to provide input. The pattern might be something simple like the Invoke pattern, which lets clients invoke a control.

Pattern Interfaces:

Each pattern is its own interface. When you implement a pattern you must implement all the properties and entire interface correctly or it will not compile properly.

Automation Events:

Automation events notify applications of changes to and actions taken with automation elements. Unlike WinEvents, UIA events are not based on a broadcast mechanism. UIA clients register for specific event notifications and can request that specific UIA properties and control pattern information be passed into their event handlers. In addition, a UIA event contains a reference to the element that raised it. Providers can improve performance by raising events selectively, depending on whether any clients are listening.

Test Before Implementing Events: Test your code before implementing events. This allows for tweaks to the code before adding the additional complexity of events to it.

Control Type:

Automation elements expose common properties of the UI elements they represent. One of these properties is the UIA control type, which defines its basic appearance and functionality as a single recognizable entity, for example, a button or check box.

In UI Automation, a control type is a set of conditions that a control must meet in order to use the ControlTypeProperty property. The conditions include specific guidelines for UIA tree structure, UIA property values, control patterns, and UIA events.

Select the correct control type for your control by thinking through the functionality of the control, not its appearance. For example, an image that is clickable might use the Button control type, which has an invoke functionality, whereas the Image control type does not.

Control Type Flexibility: The total number of pre-defined control types is significantly lower than MSAA accRole definitions, because you can combine UIA control patterns to express a larger set of features while MSAA roles cannot. You can also customize the description of control type by LocalizedControlType property while keeping the baseline type as defined.

Additional information about the Microsoft UI Automation Community Promise and UI Automation Specification can be found on MSDN.

Required UIA Properties: Name and Automation ID

Certain automation properties can be left as default. However, when creating custom controls, there are two required properties that must be set directly by the developer. These are:

  • Name
  • Automation ID

Name

The Name property should always be consistent with the label text on screen. For example, the Name must be “Browse…” for the button element with “Browse…” as the label. Do not include the control role or type information such as “button” or “list” in the Name. This will cause conflict with the text from LocalizedControlType property. The control type and control patterns are responsible for describing the functionality, not the Name, of an element.

When the corresponding label text is not visible on screen or when it is replaced by graphics, alternative text (alt text) should be chosen. Good alt text is a concise description of the UI function or the feature as if it was labeled by the simple text. For example, the Windows Start menu button should have the Name “Start” instead of “Windows Logo on blue round sphere graphics.” It is a good idea, when doing spec reviews, to ensure alt text is defined for any graphics or images.

Control Type

The Control Type is a well-known identifier within UI Automation that indicates what kind of control an element represents, such as a combo box or a button. Having a well-known identifier makes it easier for AT software to determine what types of controls are available in the user interface, and how to interact with the controls. Choose your control type by reviewing the possible choices in the UIA Control Type enumeration, and then selecting the one that best represents your control.

If you pick a standard control type, you do not need to define a Localized Control Type; the UI Automation system will provide a localized form of the control type automatically. If you choose the Custom control type, please define a Localized Control Type using a sort, localized name for the type of control you are creating.

Each control type has a standard set of control properties, patterns, and events that must be implemented. For example, elements with the Combo Box control type always support the ExpandCollapse and Selection control patterns and their associated events. Choosing a control type is an important decision, since it gives you a further set of requirements for your implementation. Further information about Control Types is available on MSDN.

Automation ID

The Automation ID uniquely identifies a UIA Element within its siblings. Having a unique identifier is important for both assistive technologies and test automation frameworks. The Automation ID is ideally a unique identity for the entire Logical Hierarchy, but is required to be unique within its siblings. For example, an application may contain a menu control with multiple top-level menu items that, in turn, have multiple child menu items. These secondary menu items may be identified by a generic scheme such as "Item1", "Item 2", and so on, allowing duplicate identifiers for children across top-level menu items. However, giving each item a completely unique name or string within the product allows for easier identification. The Automation ID is noted in the code.

Information Icon: Automation ID in Other Languages The Automation ID does not change regardless of language. A button name might be different in French, but the Automation ID retains the original name given by the developer.

The code for implementing Name and Automation ID are similar. Here is a sample of what your code might look like:

IFACEMETHODIMP MyCustomControlProvider::







    GetPropertyValue(PROPERTYID idProp, 







    VARIANT * pRetVal )







{







    pRetVal->vt = VT_EMPTY;







    if(idProp == UIA_NamePropertyId)







    {







        pRetVal->bstrVal = SysAllocString(L"MyControlName");







        pRetVal->vt = VT_BSTR;







    }















    else if(idProp == UIA_AutomationIdPropertyId)







    {







        pRetVal->bstrVal = SysAllocString(L"MyControlId");







        pRetVal->vt = VT_BSTR;







    }

Note: pRetVal [out, retval] - When this method returns, contains the property value, or NULL if the property is not supported by this provider. This parameter is passed uninitialized.

Designing a Custom Control

Designing and implementing a custom UI Automation solution is a three step process:

  1. Create the logical hierarchy. The Logical Hierarchy for Product X is available below, with further information available in the Product X Logical Hierarchy. The Logical Hierarchy will serve as the basis for laying out the UIA information needed to write the supporting Win32 code for Product X.
    First, you need to review Product X and the Logical Hierarchy.
  2. Determine the appropriate control type. Once you have mapped out the Logical Hierarchy for your application and determined where keyboard navigation needs to be implemented, you need to determine the components for your custom control. Using Product X’s data grid control type as an example, we can chart out the major properties for the control that must be implemented.
  3. Determine the appropriate control patterns and properties for each automation element. The Data Grid Control Type contains Control Patterns, such as Scroll Pattern, and all the interface properties and methods for those Control Patterns. The Control Type control properties, such as Automation ID and Name, and Control Type UI Automation Events, such as AutomationF, and Control Type UI automation Events, such as AutomationFocusChangedEvent are also all listed out so that the properties needed for the code are easily included in the application.

Use Common Controls Where Viable Many scenarios can be accomplished with common controls and still be usable, accessible, and aesthetic. Prior to creating a custom control application, confirm that you cannot accomplish your scenarios with the controls provided by the platform. This will save time and money for your finished product.

Implementing a Custom Control

Each control type has its own set of patterns, methods, events, and properties that need to be implemented for the control to compile and function correctly. Now that the control type components for the information grid in Product X have been planned out, the tactics for implementing the control in an accessible manner are much simpler. For each item in the control chart, be sure to implement the correct UIA properties. The process for creating a custom control from scratch will now be addressed.

Step 1: Exposing the Provider

When implementing UIA in Win32, developers must include the Uiautomation.h header in the application. Also, in order to set up UIA to work in your custom control, you must set up an object that implements the UIA API to be returned when WM_GETOBJECT is called. The way this is done in Win32 is by hooking WMGETOBJECT to implement the correct core provider interface, which in this case is IRawElementProviderSimple.

UI Automation provider interfaces are much easier to implement than IAccessible, and they can be implemented by managed or unmanaged providers. The interfaces expose the same basic information as IAccessible (locations, tree structure, focus and hit testing support). Code that implements these interfaces are visible to UI Automation clients, but is also made available as IAccessible implementations for in-process or out-of-process MSAA clients. These methods are also used by clients to access property and pattern values/support information.

Every UIA provider must implement one of the following core provider interfaces for accessibility:

InterfaceDescription
IRawElementProviderSimpleThis interface represents an instance of a UI element, and it has methods that expose property values and pattern interfaces. All elements exposed to UI Automation must implement this interface at minimum.
IRawElementProviderFragmentAdds functionality for an element in a complex control, including navigation within the fragment, setting focus, and returning the bounding rectangle of the element.
IRawElementProviderFragmentRootAdds 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.

For Product X, the data grid custom control requires that IRawElementProviderFragmentRoot and IRawElementProviderFragment also be implemented. Any custom control which contains automation elements within it, such as the data grid, needs to have these providers implemented in order to create an accurate UI Automation Tree.

Sample code for exposing the provider:

IFACEMETHODIMP MyCustomControlProvider ::







    get_ProviderOptions(ProviderOptions * pRetVal)







{







    *pRetVal = ProviderOptions_ServerSideProvider







        | ProviderOptions_UseComThreading;







    return S_OK;







}















Sample for WMGETOBJECT















LRESULT MyCustomControlWndProc(HWND hwnd, UINT uMsg, 







    WPARAM wParam, LPARAM lParam)







{







    switch(uMsg)







    {







        case WM_GETOBJECT:







        {







            MyCustomControlProvider * pProvider = 







                new MyCustomControlProvider();







            LRESULT lres = UiaReturnRawElement(hwnd, wParam, 







                lParam, pProvider);







            pProvider->Release();







            return lres;







        }







    }







    return DefWindowProc(hwnd, uMsg, wParam, lParam);







}

Step 2: Implementing UIA properties

Once the API is set up, you can then begin implementing the control types and the corresponding properties from your control chart. To show how this is done, we can expand the DataGrid control type portion of the control chart to see that we will need to implement. The code must include the correct:

  • Control types
  • Patterns
  • Pattern properties
  • Pattern methods
  • Control properties
  • UI Automation events

Here is a breakdown of the properties for the DataGrid control type for Product X:

1. ControlType DataGrid

     a. ControlPatterns

          i. GridPattern

               1. IGridProvider Interface Properties

                    a. ColumnCount

                    b. GetItem

                    c. RowCount

          ii. ScrollPattern

               1. IScrollProvider Interface Properties

                    a. HorizontallyScrollable

                   b. HorizontalScrollPercent

                   c. HorizontalViewSize

                   d. VerticallyScrollable

                   e. VerticalScrollPercent

                    f. VerticalViewSize

               2. IScrollProvider Interface Methods

                    a. Scroll

                    b. SetScrollPercent

          iii. SelectionPattern

               1. ISelectionProvider Interface Properties

                   a. CanSelectMultiple

                    b. IsSelectionRequired

               2. ISelectionProvider Interface Methods

                    a. GetSelection

          iv. TablePattern

               1. ITableProvider Interface Properties

                    a. RowOrColumnMajor

               2. ITableProvider Interface Methods

                    a. GetColumnHeaders

                    b. GetRowHeaders

     b. ControlProperties

               i. Automation ID

               ii. BoundingRectangle

               iii. ClickablePoint

               iv. IsContentElement

               v. IsControlElement

               vi. IsKeyboardFocusable

               vii. LabeledBy (not needed)

               viii. LocalizedControlType=”data grid”

               ix. Name

     c. UI Automation Events

               i. AutomationFocusChangedEvent

               ii. BoundingRectangleProperty property-changed event

               iii. IsEnabledProperty property-changed event

               iv. IsOffscreenProperty property-changed event

               v. LayoutInvalidatedEvent

               vi. StructureChangedEvent

               vii. CurrentViewProperty property-changed event

               viii. HorizontallyScrollableProperty property-changed event

               ix. HorizontalScrollPercentProperty property-changed event

               x. HorizontalViewSizeProperty

               xi. VerticallyScrollableProperty property-changed event

               xii. VerticalScrollPercentProperty property-changed events

               xiii. VerticalViewSizeProperty property-changed event

              xiv. InvalidatedEvent

Step 3a: Implementing Patterns

Providers support control patterns by implementing interfaces that are appropriate to the control type. For example, the provider for a button control must implement IInvokeProvider. A pointer to the object that implements this interface (it may be same object that implements IRawElementProviderSimple) is returned by IRawElementProviderSimple::GetPatternProvider.

In the case of Product X, we know one piece of the control is created using the DataGrid control type. Since this control is scrollable, four patterns will need to be implemented:

Control Pattern ClassProvider InterfaceDescription
GridIGridProviderUsed for controls that support grid functionality such as sizing and moving to a specified cell. For example, the large icons view in Windows Explorer or simple tables without headers in Microsoft Word.
ScrollIScrollProviderUsed for controls that can scroll. For example, a control that has scroll bars that are active when there is more information than can be displayed in the viewable area of the control.
SelectionISelectionProviderUsed for selection container controls. For example, list boxes and combo boxes. Table ITableProvider Used for controls that have a grid as well as header information. For example, Microsoft Excel worksheets.
TableITableProviderUsed for controls that have a grid as well as header information. For example, Microsoft Excel worksheets.

For more information on which patterns are required for your control type are available on MSDN.

Implement each pattern and its properties for the control type you wish to use. To begin implementing a pattern, the Toggle pattern for example, the code would look like this:

}







HRESULT CCheckbox3UIAProxy::get_ToggleState(ToggleState* pRetVal)







{







    HRESULT hr = CheckAlive();







    if (SUCCEEDED(hr))







    {







        switch(_pControl->CheckboxCheckedState())







        {







        case STATE_SYSTEM_CHECKED:







            {







                *pRetVal = ToggleState_On;







            }







            break;







        case STATE_SYSTEM_MIXED:







            {







                *pRetVal = ToggleState_Indeterminate;







           }







            break;







        case 0:







            {







                *pRetVal = ToggleState_Off;







            }







            break;







        }







    }







    return hr;







}

Code sample taken from MSDN.

In order to get patterns, the following code would be implemented:

HRESULT CCheckbox3UIAProxy::GetPatternProvider(















  PATTERNID patternId,















  IUnknown **pRetVal)















{















  // Clear out param















  *pRetVal = NULL; 















 















  if (patternId == UIA_TogglePatternId)















  {















    *pRetVal = 















    static_cast<IRawElementProviderSimple*>(this);















    AddRef();















  }















  return hr;















}

Code sample taken from Code magazine article Making Custom Controls Accessible.

Step 3b Implementing Pattern Properties & Methods

Pattern Properties

Each pattern for the control has properties and methods that need to be implemented for them. In the case of Product X, let’s explore the Selection pattern.

In the case of Selection, there are two properties that must be implemented:

  • CanSelectMultiple – gets a value that specifies whether the UIA provider allows more than one child element to be selected concurrently
  • IsSelectionRequired – gets a value that specifies whether the UIA provider requires at least one child element to be selected

Select the provider for your pattern for more information on the required properties and methods for your pattern type.

Pattern Methods

Methods provide a means of supporting how the process of a pattern is implemented, such as GetColumnHeaders or SetScrollPercent. Using the Selection pattern again, there is one required method for the ISelectionProvider interface, which is GetSelection. GetSelection retrieves a UIA provider for each child element that is selected.

Code for implementing the toggle method would look like this:

HRESULT CCheckbox3UIAProxy::Toggle()







{







    HRESULT hr = CheckAlive();







    if (SUCCEEDED(hr))







    {







        _pControl->MoveToNextState();







    }







    return hr;

Note: MoveToNextState is a function that will actually perform the action of toggling. It will change the UI as well as invoke the action associated with toggling.

Step 4: Implementing Control Properties

Once you have implemented all of the appropriate control patterns for your control type, you should then move on to implementing the required control properties for it. As with all controls using UIA, this control requires a Name and Automation ID. In addition, the DataGrid control type also needs the following properties implemented:

  • Automation ID
  • BoundingRectangle
  • ClickablePoint
  • IsContentElement
  • IsControlElement
  • IsKeyboardFocusable
  • LabeledBy (Not Needed)
  • LocalizedControlType=”data grid”
  • Name

The code for implementing a property, such as IsContentElement, would look like this:

else if ( propertyId == UIA_IsContentElementPropertyId)







{







    pRetVal->vt = VT_BOOL;







    pRetVal->boolVal = VARIANT_TRUE;







}

Step 5: Checking In

Once you have implemented a control type, the patterns, methods, and pattern and control properties you should spend a little time doing unit testing to ensure your code will expose correctly to AT. UISpy (add link to this) or another UI Automation test tool will allow you to double check that your code is exposing the correct information. Be aware that some functionalities of the testing will be limited because events are not yet implemented. Once you’ve done unit testing to ensure Name, Automation ID, and other information is reporting properly, you can move onto step 6.

Step 6: Implementing UIA Events

The purpose of an event is to notify the AT of something taking place in the UI. For example, the event identified by Invoke_InvokedEvent_GUID should be raised whenever the control is invoked, either through direct user input or by the client application through the control pattern.

To optimize performance, a provider can selectively raise events, or raise no events at all if no client application is registered to receive them. You can call the UiaClientsAreListening function to ascertain whether AT has subscribed to any events. For more precise information about what events are of interest to clients, implement the IRawElementProviderAdviseEvents interface in your provider object. The methods of this interface will be called whenever a client subscribes or unsubscribes to an event.

For the DataGrid control type, the following events are required:

  • AutomationFocusChangedEvent
  • BoundingRectangleProperty property-changed event
  • IsEnabledProperty property-changed event
  • IsOffscreenProperty property-changed event
  • LayoutInvalidatedEvent
  • StructureChangedEvent
  • CurrentViewProperty property-changed event
  • HorizontallyScrollableProperty property-changed event
  • HorizontalScrollPercentProperty property-changed event
  • HorizontalViewSizeProperty
  • VerticallyScrollableProperty property-changed event
  • VerticalScrollPercentProperty property-changed events
  • VerticalViewSizeProperty property-changed event
  • InvalidatedEvent

Supporting Navigation for Custom Controls

When you implement a provider for a complex custom control using UIA you must support navigation between the root node of the fragment and its descendants, and between sibling nodes.

Navigation of elements within a fragment must be supported. The fragment root supports navigation to its children, which descendants support navigation in a variety of directions including: back to the parent, to their next and previous siblings, and to any children they may have. Each element in the complex custom control needs to have IRawProviderFragment implemented for it, and the root will also require IRawProviderFragmentRoot to be implemented in addition to the fragment provider. The structure of a fragment is determined by your implementation of IRawElementProviderFragment::Navigate.

Correcting Siblings as Parent/Child Relationships When two or more elements are each contained in a UIA fragment, and are each contained in a window, the UIA Tree shows the HWNDs as siblings by default rather than a parent-child relationship. To correct this, reposition the HWND-hosted element as a child of the root element by implementing IRawElementProviderHwndOverride. When IRawElementProviderHwndOverride::GetOverrideProviderForHwnd is called for the child element's HWND, return your custom provider for that element.

Registering Completely Custom Controls

If you create controls with functionality that cannot be represented by currently provided control patterns or properties, UIA offers further extensibility through the registration of custom control patterns, properties, and events.

Creating a custom UI Automation control pattern, property, and/or event requires careful planning, implementation, and testing. Here are the steps you would follow to do so:

  1. Consult the UI Automation Community Promise to confirm that there is no sufficient pattern and property to describe the functionality of your control.
  2. If, upon review, you still decide to create a custom pattern or property, then consult the UI Automation Specification on the proper way to implement the functionality.
  3. Follow the API registration process described in the UIA Community Promise.
  4. Publish your custom implementation for other applications and clients to view.

Registration

A custom property, event, or pattern is specified by filling out a UIAutomationPropertyInfo, UIAutomationEventInfo or UIAutomationPatternInfo struct as appropriate, and then calling the appropriate Register method on the IUIAutomationRegistrar object. This returns an integer property, event or pattern identifier that can then be used in any UI Automation API that uses such an identifier. For example, the PropertyId returned by RegisterProperty can then be used in IUIAutomationElement::GetCurrentPropertyValue or in IUIAutomation::CreatePropertyCondition.

The following must be specified (in the appropriate structure) for a custom item:

  • A Globally Unique Identifier GUID that uniquely identifies the property, event, or pattern. Note that the GUID is the real identifier of the item; two properties or events or patterns are considered equivalent if they have the same GUID. The integer identifier that is returned by the Register…() method is temporary and only valid within and for the remainder of the lifetime of the UI Automation client that called it. It may return different integer values for the same GUID when called over different runtime instances of the client.
  • A unique and permanent string that represents the programmatic name for the item. This is used only for debugging purposes.

Custom Properties

Custom properties require a value identifying the type of the property, for example, whether it is an integer or a string, to be specified.

In addition, custom patterns also require the following to be specified:

  • An array of events associated with the pattern
  • An array of properties associated with the pattern
  • IIDs, which describe a GUID structure used to describe an identifier for a MAPI interface, of the pattern’s corresponding provider interface and client interface
  • An array of methods associated with the pattern, for each of which the count of arguments and argument types must also be specified
  • Code to create a client interface object
  • Code to perform marshalling for the pattern’s properties and methods (Client Wrapper and Pattern Handler object)

How Clients and Providers Support Custom Control Patterns

In order to take advantage of newly registered control pattern, both clients and providers are required to supply a small amount of support code.

Clients use a client interface object (for example, an IUIAutomationCustomPattern interface) that has getter functions for cached and current properties, as well as methods.

Providers implement a provider interface (for example, an ICustomProviderinterface) that has getters and setters for each property, as well as methods.

To support the client API object, the code that registers a pattern must supply a factory for creating instances of a Client Wrapper. This wrapper implements the client API as a COM interface and forwards all the property getter requests and methods calls to an IUIAutomationPatternInstance that is provided by UI Automation. The UI Automation framework then takes care of the rest of marshalling the call.

On the provider side, the code that registers a pattern must also supply a “pattern handler” object that performs the reverse function of the Client Wrapper. The UI Automation Framework forwards the property and method requests to the pattern handler object. The pattern handler then calls the appropriate method on the target object’s provider interface.

Client/Provider Communication

The UI Automation framework takes care of all communication between the client and provider, both of which register corresponding control pattern interfaces. The Client Wrapper and Pattern Handler need to map only between C++ interface methods calls with positional arguments and parameters.

Tools & Resources

Now that you’ve learned more about developing accessible products and services, you may want to explore the subject further. Here are some online resources you might find helpful.

Win32-related Links

Course Completion

Congratulations, you have completed the Windows (Win32) Accessibility for Developers course.

You now have an increased understanding on how to use MSAA and UIA in Win32 to create programmatic access for users of Assistive Technology (AT). You can also see the benefits of including accessibility into your product development cycle in a programmatic way to enhance the development process. Further courses are available on how to write accessible code and address platform-specific issues for accessibility. You can consult the Microsoft Accessibility developer portal on MSDN for more information.

The courses in the Accessibility Training for Developers include:

  1. Introduction to Accessibility
  2. General Accessibility for Developers
  3. Windows (Win 32) Accessibility for Developers
  4. WinForms Accessibility for Developers
  5. WPF Accessibility for Developers
  6. Silverlight Accessibility for Developers
Rate: