Skip to main content

WPF Accessibility for Developers

Welcome to the WPF Accessibility for Developers course. This course is tailored to your role as a developer using WPF and UI Automation (UIA), 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 UIA and WPF work to create accessible code
  • Implement UI design for accessibility

Welcome

It is assumed that you are already somewhat familiar with the Windows Presentation Foundation (WPF) framework.

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

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

Programmatic Access

Windows Presentation Foundation (WPF) uses the newest accessibility application programming interface (API), UI Automation (UIA), to programmatically expose information to users of assistive technology (AT). Developers use the WPF code, as well as its declarative markup language XAML, to create products with rich functionality.

Making WPF applications and products accessible requires a clear understanding of UIA and how it works.

UI Automation Basics

UIA is a new accessibility framework, that 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 UI 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

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 suplly 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.

Info icon: 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 wit 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.

Pitfall: 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.

Info icon: 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 Properties: Name and Automation ID

When writing the code there are two required properties that must be set directly by the developer in the markup rather than allowed to default in order be accessible. These are:

  • Name
  • Automation ID

Name

The Name for an automation element is assigned by the developer. 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.

Alt Text

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.”

This is set in the code like this, for example:

<ListBox AutomationProperties.Name = “States”/>

Automation ID

The Automation ID exclusively identifies a UIA Element within its siblings and the product. 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 XAML side of the code.

Below is an example of the code to add the Name and Automation ID looks 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_ControlTypePropertyId)



    {



        pRetVal->lVal = UIA_ButtonControlTypeId;



        pRetVal->vt = VT_I4;



    }



    return S_OK;



}

Language and Automation ID: 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.

Common Controls

Implementing accessibility using WPF common controls is simple. If the common control is implemented correctly, then the only additional work required for accessibility is ensuring that the Name and Automation ID have been entered into the XAML code. The UIA provider will automatically expose the tree, automation elements, properties, methods and events to the client.

Required Steps

In order to implement UIA in WPF, developers must start by completing the three steps described in the table below.

StepDescription
1. Add UI Automation references

Developers must add the following UI Automation DLLs:

  • UIAutomationClient.dll. Provides access to the UI Automation client APIs.
  • UIAutomationClientSideProvider.dll. Provides the ability to automate Win32 controls and to automate the WPF controls that interact with Win32 features.
  • UIAutomationTypes.dll. Provides access to the types that are defined in UI Automation.
2. Add the System.Windows.Automation namespaceThis namespace contains everything that UI Automation clients need in order to use the capabilities of UI Automation, except text handling.
3. Add the System.Windows.Automation.Text namespaceThis namespace contains everything that UI Automation clients need in order to use the text-handling capabilities of UI Automation.

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.

The KeyboardNavigation class is responsible for implementing default keyboard focus navigation when one of the navigation keys is pressed. The navigation keys are: TAB, SHIFT+TAB, CTRL+TAB, CTRL+SHIFT+TAB, UPARROW, DOWNARROW, LEFTARROW, and RIGHTARROW keys.

The navigation behavior of a navigation container can be changed by setting the attached KeyboardNavigation properties TabNavigation, ControlTabNavigation, and DirectionalNavigation. These properties are of type KeyboardNavigationMode and the possible values are Continue, Local, Contained, Cycle, Once, and None. The default value is Continue, which means the element is not a navigation container.

The following example creates a Menu with a number of MenuItem objects. The TabNavigation attached property is set to Cycle on the Menu. When focus is changed using the tab key within the Menu, focus will move from each element and when the last element is reached focus will return to the first element.

Menu navigationMenu = new Menu();
MenuItem item1 = new MenuItem();
MenuItem item2 = new MenuItem();
MenuItem item3 = new MenuItem();
MenuItem item4 = new MenuItem();
navigationMenu.Items.Add(item1);
navigationMenu.Items.Add(item2);
navigationMenu.Items.Add(item3);
navigationMenu.Items.Add(item4);
KeyboardNavigation.SetTabNavigation(navigationMenu,
KeyboardNavigationMode.Cycle);

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, you guarantee that the presentation of 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.

WPF is dpi-aware, but you must still avoid hardcoding layouts, size, or font, and instead use percentages where possible to ensure the scaling works for all users.

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. Be sure not to hard-code colors and have alternative color icons available for high-contrast setting users.

Other Considerations

There are two additional accessibility considerations that you need to think through when developing your product. These are color and sound.

Color

Text and background contrasts should 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.

Custom Controls

Most custom controls in WPF are actually derived from common controls. When implementing a custom control, you must override the "Core" methods from the base automation peer class that describe behavior unique and specific to your custom control. Before you can assign a control type to an element, the element needs to meet certain conditions, including a particular automation tree structure, property values, control patterns, and events; however, you are not limited to using only these sets of conditions. You can extend a control with additional patterns and properties.

In order to create a customized control with UIA support, there are five main steps to follow:

  1. Expose the control to UIA by overriding OnCreateAutomationPeer
  2. Provide correct property values by overriding Core methods
  3. Enable the client to interact with the control using methods
  4. Implement Pattern providers
  5. Raise events

Control Types and Control Patterns: There is not a one-to-one correspondence between control types and control patterns. A control pattern may be supported by multiple control types, and a control may support multiple control patterns, each of which exposes different aspects of its behavior. Some controls have conditional support for several control patterns. For example, the menu item control has conditional support for the Invoke, Expand Collapse, Toggle, and SelectionItem control patterns, depending on the menu item control’s function within the menu control.

Know Before you Go

WPF supports UIA through a tree of peer automation objects that parallels the tree of user interface elements. Test code and applications that provide accessibility features can use automation peer objects directly (for in-process code) or through the generalized interface provided by UIA.

In order to understand the customization process in UIA, it is important to have a clear understanding of automation peers and their role in UIA. Some customized controls require custom automation peer implementation when a single automation peer-derived control is insufficient to convey functionality. This section introduces the following concepts about Automation Peers:

  • Automation Peer Classes
  • Peer Navigation
  • Customizations in a Derived Peer

Automation Peer Classes

WPF controls support UI Automation through a tree of peer classes that derive from AutomationPeer. By convention, peer class names begin with the control class name and end with "AutomationPeer". For example, ButtonAutomationPeer is the peer class for the Button control class. The peer classes are roughly equivalent to UI Automation control types but are specific to WPF elements.

Elements implement an automation peer class if they accept interface activity from the user, or if they contain information needed by users of AT. Not all WPF visual elements have automation peers. A few examples of classes that implement automation peers are Button, TextBox, and Label. Examples of classes that do not implement automation peers are classes that derive from Decorator, such as Border, and classes based on Panel, such as Grid and Canvas.

Custom Peer Classes: The base Control class may not have a corresponding peer class. If you need a peer class to correspond to a custom control that derives from a control that does not have a corresponding peer class, you should derive the custom peer class from FrameworkElementAutomationPeer. For example, if you are deriving a custom control from Button and you require a custom automation peer that has the traits of a button automation peer, then you should derive from the button automation peer. If, however, you need a button that pulls out into a 3-D picture, for example, then the FrameworkElementAutomationPeer would be a better choice to derive from.

Customizations in a Derived Peer

If you are creating a custom automation peer, you will need to override the default automation peer for your control. All classes that derive from UIElement and ContentElement contain the protected virtual method OnCreateAutomationPeer. WPF calls OnCreateAutomationPeer to get the automation peer object for each control. Automation code can use the peer to get information about a control’s characteristics and features and to simulate interactive use.

A custom control that supports automation must override OnCreateAutomationPeer and return an instance of a class that derives from AutomationPeer. For example, if a custom control derives from the ButtonBase class, then the object returned by OnCreateAutomationPeer should derive from ButtonBaseAutomationPeer.

Peer Navigation

The UIA tree for a custom peer should be correct by default. After locating an automation peer, in-process code can navigate the peer tree by calling the object's GetChildren and GetParent methods. Navigation among WPF elements within a control is supported by the peer's implementation of the GetChildrenCore method. The UI Automation system calls this method to build up a tree of subelements contained within a control; for example, list items in a list box. The default UIElementAutomationPeer.GetChildrenCore method traverses the visual tree of elements to build the tree of automation peers.

Custom controls override this method to expose children elements to automation clients, returning the automation peers of elements that convey information or allow user interaction. When creating a customized control using UIA it is important to be aware of this navigation process.

Expose the Control to UIA

When UIA queries the root of a WPF application about an element, the root returns the AutomationPeer for that element. Standard WPF controls implement default properties for the AutomationPeer and are supplemented by developers with additional non-default properties. Custom controls require further work to ensure that programmatic access is correctly implemented. The AutomationPeer provides information about the element to AT such as the control type, properties, and patterns. It also raises events when the client registers to listen for them.

If you create custom controls, you need a way to expose these control patterns and properties. Using automation peers to do this in WPF, you have two options:

  • Inherit an automation peer that has the control patterns and properties which sufficiently describe your control.
  • Create your own automation peer that implements the control patterns and properties you need.

Override the OnCreateAutomationPeer method for your custom control so that it returns your provider object, which must derive directly or indirectly from AutomationPeer. For example, if a custom control that is derived from Button is named MyButton, the object returned by OnCreateAutomationPeer should be MyButtonAutomationPeer.

Security Considerations for Derived Peers: When overriding an automation peer you must use the correct assembly. Automation peers must run in a partial-trust environment. Code in the UIAutomationClient assembly is not configured to run in a partial-trust environment, and automation peer code should not reference that assembly. Instead, you should use the classes in the UIAutomationTypes assembly. For example, you should use the AutomationElementIdentifiers class from the UIAutomationTypes assembly, which corresponds to the AutomationElement class in the UIAutomationClient assembly. Reference the UIAutomationTypes assembly in automation peer code.

Provide Property Values

Automation code gets information about your control by calling public methods of the peer class. To provide information about your control, override each method whose name ends with "Core" when your control implementation differs from that of that provided by the base automation peer class. At a minimum, your control must implement the GetClassNameCore and GetAutomationControlTypeCore methods, as shown in the following example:

protected override string GetClassNameCore()



{



    return "NumericUpDown";



}



protected override AutomationControlType GetAutomationControlTypeCore()



{



    return AutomationControlType.Spinner;



}

ControlType Values: Your implementation of GetAutomationControlTypeCore describes your control by returning a ControlType value. Although you can return ControlType.Custom, you should return one of the more specific control types if it accurately describes your control. A return value of ControlType.Custom requires extra work for the provider to implement UI Automation, and UI Automation client products are unable to anticipate the control structure, keyboard interaction, and possible control patterns.

Filter the UI Automation Tree

When elements appear in the UIA Tree that are not desired as a result of a custom automation peer, there are ways to filter information for your users. Implement the IsContentElementCore and IsControlElementCore methods to indicate whether your control contains data content or fulfills an interactive role in the user interface (or both). By default, both methods return True. These settings improve the usability of AT, which may use these methods to filter the automation tree.

If your GetPattern method transfers pattern handling to a subelement peer, the subelement peer's IsControlElementCore method can return False to hide the subelement peer from the automation tree. For example, scrolling in a ListBox is handled by a ScrollViewer, and the automation peer for PatternInterface.Scroll is returned by the GetPattern method of the ScrollViewerAutomationPeer that is associated with the ListBoxAutomationPeer. Therefore, the IsControlElementCore method of the ScrollViewerAutomationPeer returns false, so that the ScrollViewerAutomationPeer does not appear in the automation tree.

Automation Properties: Your automation peer should provide appropriate default values for your control. Note that XAML that references your control can override your peer implementations of core methods by including AutomationProperties attributes. For example, the following XAML creates a button that has two customized UI Automation properties.

<Button AutomationProperties.Name="Special"

AutomationProperties.HelpText="This is a special button."/>

Enable the Client to Interact with the Control Using Methods

Automation peers simplify some implementation aspects of server-side UI Automation providers, but custom control automation peers must still handle pattern interfaces. Like non-WPF providers, peers support control patterns by providing implementations of interfaces in the System.Windows.Automation.Provider namespace, such as IInvokeProvider. The control pattern interfaces can be implemented by the peer itself or by another object.

The peer's implementation of GetPattern returns the object that supports the specified pattern. UI Automation code calls the GetPattern method and specifies a PatternInterface enumeration value. Your override of GetPattern should return the object that implements the specified pattern.

If your control does not have a custom implementation of a pattern, you can call the base type's implementation of GetPattern to retrieve either its implementation or null if the pattern is not supported for this control type. For example, a custom NumericUpDown control can be set to a value within a range, so its UI Automation peer would implement the IRangeValueProvider interface. The following example shows how the peer's GetPattern method is overridden to respond to a PatternInterface.RangeValue value.

public override object GetPattern(PatternInterface patternInterface)



{



    if (patternInterface == PatternInterface.RangeValue)



    {



        return this;



    }



    return base.GetPattern(patternInterface);



}

A GetPattern method can also specify a subelement as a pattern provider. The following code shows how ItemsControl transfers scroll pattern handling to the peer of its internal ScrollViewer control.

public override object GetPattern(PatternInterface patternInterface)



{



    if (patternInterface == PatternInterface.Scroll)



    {



        ItemsControl owner = (ItemsControl) base.Owner;



        // ScrollHost is internal to the ItemsControl class



        if (owner.ScrollHost != null)



        {



            AutomationPeer peer = UIElementAutomationPeer.CreatePeerForElement(owner.ScrollHost);



            if ((peer != null) && (peer is IScrollProvider))



            {



                peer.EventsSource = this;



                return (IScrollProvider) peer;



            }



        }



    }



    return base.GetPattern(patternInterface);



}

To specify a subelement for pattern handling, this code gets the subelement object, creates a peer by using the CreatePeerForElement method, sets the EventsSource property of the new peer to the current peer, and returns the new peer. Setting EventsSource on a subelement prevents the subelement from appearing in the automation peer tree and designates all events raised by the subelement as originating from the control specified in EventsSource. The ScrollViewer control does not appear in the automation tree, and scrolling events that it generates appear to originate from the ItemsControl object.

Implement Pattern Providers

The interfaces implemented by a custom provider are explicitly declared if the owning element derives directly from Control. For example, the following code declares a peer for a Control that implements a range value.

public class RangePeer1 : FrameworkElementAutomationPeer, IRangeValueProvider { }

If the owning control derives from a specific type of control such as RangeBase, the peer can be derived from an equivalent derived peer class. In this case, the peer would derive from RangeBaseAutomationPeer, which supplies a base implementation of IRangeValueProvider. The following code shows the declaration of such a peer.

public class RangePeer2 : RangeBaseAutomationPeer { }

Raise Events

Automation clients can subscribe to automation events. Custom controls must report changes to control state by calling the RaiseAutomationEvent method. Similarly, when a property value changes, call the RaisePropertyChangedEvent method.

The code below shows how to get the peer object from within the control code and call a method to raise an event. As an optimization, the code determines if there are any listeners for this event type. Raising the event only when there are listeners avoids unnecessary overhead and helps the control remain responsive.

if (AutomationPeer.ListenerExists(AutomationEvents.PropertyChanged))



{



    NumericUpDownAutomationPeer peer = 



        UIElementAutomationPeer.FromElement(nudCtrl) as NumericUpDownAutomationPeer;



 



    if (peer != null)



    {



        peer.RaisePropertyChangedEvent(



            RangeValuePatternIdentifiers.ValueProperty,



            (double)oldValue,



            (double)newValue);



    }



}

Raise Events from a UI Automation Provider

The example code in this section demonstrates how a UI Automation event is raised in the implementation of a custom button control. The implementation enables a UI Automation client application to simulate a button click. To avoid unnecessary processing, the example checks ClientsAreListening to see whether events should be raised.

/// Responds to a button click, regardless of whether it was caused by a mouse or







/// keyboard click or by InvokePattern.Invoke. 







private void OnCustomButtonClicked()







{







// TODO Perform program actions invoked by the control.







 







// Raise an event.







if (AutomationInteropProvider.ClientsAreListening)







{







AutomationEventArgs args = new AutomationEventArgs(InvokePatternIdentifiers.InvokedEvent);







AutomationInteropProvider.RaiseAutomationEvent(InvokePatternIdentifiers.InvokedEvent, this, args);







}







}

Additional Custom Control Guidelines

Developers should remember the following points when they create a custom control with Automation support.

  • Child and Peer Element Navigation If the custom control's UI includes a collection of standard controls (for example, Button and TextBox), in the custom control's AutomationPeer class, developers should override the base peer class's GetChildrenCore or GetChildren method in order to return the collection of the standard controls' Automation peers. If the custom control derives from UIElementAutomationPeer, by default the implementation of GetChildrenCore will walk the children in the visual tree and return the list of the children’s Automation peers. If developers do not want to expose all the visual elements, they can override GetChildrenCore and return only the peers that they want.
  • Pop-Up Control Navigation If the custom control contains a Popup control, developers should not rely on walking the visual tree, because Popup content is in a separate visual tree. For example, ComboBoxAutomationPeer.GetChildrenCore should return a list of Automation peers that correspond to the elements under the Popup control's visual tree.
  • Deriving from FrameworkElementAutomationPeer If the custom control is derived from the Control class, and the Control class does not include an AutomationPeer object itself, developers should derive the custom peer class from FrameworkElementAutomationPeer.
  • Automation Event Performance When developers raise Automation events, for performance reasons they should make sure that the events are raised only when there are listeners on the clients. To do this, developers can use AutomationPeer.ListenerExists to check related events.
  • WPF and High Contrast If you create a custom control you must adhere to the system colors to support high contrast. First, check to see if high contrast is enabled, and if it is, present your colors based on the system colors.

High contrast in WPF can be accomplished in some additional steps. You must examine which high contrast theme is on and set your custom control’s colors based on that.

An example of this code would be:

<Setter Property="Selector.IsSelected" Value="{DynamicResource {x:Static SystemParameters.HighContrastKey}}" />

Further information about high contrast in WPF can be found on the devnation blog.

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.

Course Completion

Congratulations, you have completed the WPF Accessibility for Developers course.

You now have an increased understanding on how to use Windows Presentation Foundation (WPF) common controls to create more accessible applications. 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 also 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: