
Customizations in a Derived Peer
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.
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.
Override OnCreateAutomationPeer
Override GetPattern
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.
Override "Core" Methods
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;
}
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.
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 automation tools such as screen readers, 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.
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."/>
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 { }
For an example implementation, see NumericUpDown Custom Control with Theme and UI Automation Support Sample.
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 following code 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);
}
}