Making Custom Controls Accessible While custom controls are introduced every day, not all of them are easily accessible. This article provides a quick summary of Microsoft® technologies that help make Win32-based custom controls programmatically accessible. Techniques range from implementing UI Automation, to creating or overriding properties with Dynamic Annotation, to using the new IAccessibleEx interface to close the gap between UI Automation and Microsoft Active Accessibility®. The Windows® operating system offers several technologies to help make controls accessible: Microsoft Active Accessibility, UI frameworks, and UI Automation. As Table 1 shows, the most effective technology depends on how developers introduce their new controls. | " | Before investing time designing a new control or customizing a standard control, you should ask yourself: “Do I really need that control?”
| " |
For controls and frameworks written from scratch, UI Automation is recommended today. While Microsoft Active Accessibility is handy for relatively simple controls, the technology doesn’t support the complexity of modern user interfaces (UI). For controls based on common controls (such as COMCTL32.DLL or USER32.DLL), Dynamic Annotation enables modest customization. However, it may not be sufficient for significant customization or for sub-classed controls. For controls based on solid Microsoft Active Accessibility implementations, developers can use the IAccessibleEx Interface specification to enhance accessibility with new UI Automation interfaces. This article introduces the basics of using UI Automation, Dynamic Annotation, and the IAccessibleEx interface for developing controls. Quick Tips: “Do I Really Need that Control?” Before investing time in designing a new control or customizing a standard control, you should ask yourself: “Do I really need that control?” If you’re lucky, you may find a standard control that does the job. Many modern UI libraries support accessibility and customization. If you customize a standard control, try not to overload it with features. Unnecessary complexity can result in expensive accessibility support cost, not to mention reduced usability. Never underestimate the implication of a small modification. If you have to introduce major customizations, consider creating a control from scratch If you have to introduce a new control, try to follow standard practices to support UI accessibility. For example, never assume that all users have access to pointing devices, and try to use system or desktop theme colors to avoid accessibility problems with custom colors. While the focus of this article is on programmatic access to the user interface, these fundamental accessibility criteria are very important. Many accessibility practices are available in the form of industry standards (such as Section 508 of the Federal Rehabilitation Act). Some useful resources are introduced at the Microsoft Accessibility Developer Center (http://msdn.microsoft.com/en-us/accessibility/default.aspx). Part I: Implementing UI Automation This article talks about implementing UI Automation for new controls in a few simplified steps. Designing a UI Automation object model for a new user interface is straightforward if you follow a few basic principles: - Define user scenarios clearly. Actual user scenarios are helpful. During the design process, you shouldn’t rely on a single operation device such as mice, custom color, or screen size.
- Define the UI functionality as simply as possible. Is it clickable (such as a button or hyperlink)? Does it allow for selection (such as a list box)?
- Isolate fundamental UI elements by function or feature. Structure a complex UI in a way consistent with user scenarios. A keyboard navigation model is a useful guide.
- Match a UI feature from the previous step with existing controls. The UI Automation Control Type specification is a useful guideline. If you have trouble finding the correct control type, see the Control Pattern specifications and locate a control type that has the control patterns you want.
Step 1: The Foundation Note: This sample project is available for download at MSDN Online (http://msdn.microsoft.com/en-us/library/ms771315(VS.85).aspx). The Windows SDK also features two additional UI Automation provider samples. The foundation of UI Automation provider objects is the IRawElementProviderSimple interface implemented by the applications or by the UI Framework that supports UI Automation. Objects that contain a more complex tree structure should also implement IRawElementProviderFragment, and the root for this structure implements IRawElementProviderFragmentRoot. Objects whose performance is very important can use IRawElementProviderAdviseEvents to know which events they should raise. Finally, you should use the IRawElementProviderHwndOverride interface when the provider needs to reorder the automation tree. Most providers do not implement this interface. In this project, you implement only IRawElementProviderSimple, which contains the following methods: // IRawElementProviderSimple STDMETHODIMP GetPatternProvider( PATTERNID patternId, IUnknown** pRetVal);
STDMETHODIMP GetPropertyValue( PROPERTYID propertyId, VARIANT* pRetVal);
STDMETHODIMP get_HostRawElementProvider( IRawElementProviderSimple** pRetVal);
STDMETHODIMP get_ProviderOptions( ProviderOptions* pRetVal);
For the UI Automation Framework to recognize your UI Automation provider implementation, you must respond to the WM_GETOBJECT message. The UiaReturnRawElementProvider function helps produce the key to establish a connection to the UI Automation Core: case WM_GETOBJECT: { if (_pProxy == NULL) { // Create an automation element object _pProxy = CCheckbox3UIAProxy::Create(this); } return UiaReturnRawElementProvider( _hwnd, wParam, lParam, _pProxy); } break;
Now you are ready to implement the support for UI Automation properties and pattern interfaces. Step 2: Implementing the Properties and Control Patterns UI Automation providers add support for properties with the GetPropertyValue method. For control patterns, providers support the corresponding patternId for the GetPatternProvider method. | " | Many accessibility practices are available in the form of industry standards (such as Section 508 of the Federal Rehabilitation Act).
| " |
You can register the properties and control patterns with GUIDs (such as the Toggle_Pattern_GUID) that you map to the corresponding IDs with the UiaLookupId function. For existing control patterns and properties, you can find corresponding IDs in UIAutomationClient.h (such as UIA_TogglePatternId). In this example, you get the property value of a check box: HRESULT CCheckbox3UIAProxy::GetPropertyValue( PROPERTYID propertyId, IUnknown **pRetVal) { // Clear out param pRetVal->vt = VT_EMPTY; // Specify ControlType.CheckBox for the // ControlType property if (propertyId == UIA_ControlTypePropertyId) { pRetVal->vt = VT_I4; pRetVal->lVal = UIA_CheckBoxControlTypeId; } return S_OK; }
To support controls with a binary status such as the check box (checked or cleared), the Toggle Pattern is best, and the implementation is straightforward as shown in Listing 1. Here, you get the pattern provider for the toggle pattern: HRESULT CCheckbox3UIAProxy::GetPatternProvider( PATTERNID patternId, IUnknown **pRetVal) { // Clear out param *pRetVal = NULL;
if (patternId == UIA_TogglePatternId) { *pRetVal = static_cast<IRawElementProviderSimple*>(this); AddRef(); } return hr; }
|