Claude-skill-registry implementing-wpf-automation

Implements WPF UI Automation for accessibility using AutomationPeer and AutomationProperties. Use when building accessible applications or enabling screen reader support.

install
source · Clone the upstream repo
git clone https://github.com/majiayu000/claude-skill-registry
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/data/implementing-wpf-automation" ~/.claude/skills/majiayu000-claude-skill-registry-implementing-wpf-automation && rm -rf "$T"
manifest: skills/data/implementing-wpf-automation/SKILL.md
source content

WPF UI Automation Patterns

Implementing accessibility features using UI Automation framework.

1. UI Automation Overview

UI Automation Framework
├── Providers (Server-side)
│   ├── AutomationPeer (base class)
│   ├── FrameworkElementAutomationPeer
│   └── Custom AutomationPeers
├── Clients (Consumer-side)
│   ├── Screen readers (Narrator, JAWS)
│   ├── Testing tools
│   └── Custom automation clients
└── Automation Properties
    ├── AutomationProperties.Name
    ├── AutomationProperties.HelpText
    └── AutomationProperties.LabeledBy

2. AutomationProperties

2.1 Basic Properties

<!-- Name - primary identifier for screen readers -->
<Button Content="Submit"
        AutomationProperties.Name="Submit form"/>

<!-- Name for image buttons (no text content) -->
<Button AutomationProperties.Name="Close window">
    <Image Source="/Icons/close.png"/>
</Button>

<!-- HelpText - additional description -->
<TextBox AutomationProperties.Name="Email address"
         AutomationProperties.HelpText="Enter your email in format user@domain.com"/>

<!-- LabeledBy - reference to label element -->
<Label x:Name="UsernameLabel" Content="Username:"/>
<TextBox AutomationProperties.LabeledBy="{Binding ElementName=UsernameLabel}"/>

2.2 Additional Properties

<!-- AcceleratorKey - keyboard shortcut hint -->
<Button Content="_Save"
        AutomationProperties.AcceleratorKey="Ctrl+S"/>

<!-- AccessKey - mnemonic key -->
<Button Content="_File"
        AutomationProperties.AccessKey="Alt+F"/>

<!-- ItemStatus - current state information -->
<ListBoxItem AutomationProperties.ItemStatus="Selected, 3 of 10"/>

<!-- ItemType - type description for list items -->
<ListBoxItem AutomationProperties.ItemType="Email message"/>

<!-- LiveSetting - for dynamic content updates -->
<TextBlock AutomationProperties.LiveSetting="Polite"
           Text="{Binding StatusMessage}"/>

2.3 LiveSetting Values

ValueDescription
OffNo announcements
PoliteAnnounce when user is idle
AssertiveAnnounce immediately

3. Custom AutomationPeer

3.1 Basic Custom Peer

namespace MyApp.Controls;

using System.Windows;
using System.Windows.Automation.Peers;
using System.Windows.Controls;

public class RatingControl : Control
{
    public static readonly DependencyProperty ValueProperty = DependencyProperty.Register(
        nameof(Value), typeof(int), typeof(RatingControl),
        new PropertyMetadata(0));

    public int Value
    {
        get => (int)GetValue(ValueProperty);
        set => SetValue(ValueProperty, value);
    }

    public int MaxValue { get; set; } = 5;

    // Create custom AutomationPeer
    protected override AutomationPeer OnCreateAutomationPeer()
    {
        return new RatingControlAutomationPeer(this);
    }
}

public class RatingControlAutomationPeer : FrameworkElementAutomationPeer
{
    public RatingControlAutomationPeer(RatingControl owner)
        : base(owner)
    {
    }

    private RatingControl RatingControl => (RatingControl)Owner;

    // Return control type for screen readers
    protected override AutomationControlType GetAutomationControlTypeCore()
    {
        return AutomationControlType.Slider;
    }

    // Return class name
    protected override string GetClassNameCore()
    {
        return nameof(RatingControl);
    }

    // Return accessible name
    protected override string GetNameCore()
    {
        var name = base.GetNameCore();

        if (string.IsNullOrEmpty(name))
        {
            return $"Rating: {RatingControl.Value} of {RatingControl.MaxValue} stars";
        }

        return name;
    }

    // Return help text
    protected override string GetHelpTextCore()
    {
        return "Use arrow keys to change rating";
    }
}

3.2 Implementing Automation Patterns

namespace MyApp.Controls;

using System.Windows.Automation;
using System.Windows.Automation.Peers;
using System.Windows.Automation.Provider;

public class RatingControlAutomationPeer : FrameworkElementAutomationPeer,
    IRangeValueProvider
{
    public RatingControlAutomationPeer(RatingControl owner)
        : base(owner)
    {
    }

    private RatingControl RatingControl => (RatingControl)Owner;

    // Expose supported patterns
    public override object? GetPattern(PatternInterface patternInterface)
    {
        if (patternInterface == PatternInterface.RangeValue)
        {
            return this;
        }

        return base.GetPattern(patternInterface);
    }

    // IRangeValueProvider implementation
    public bool IsReadOnly => false;

    public double LargeChange => 1;

    public double SmallChange => 1;

    public double Maximum => RatingControl.MaxValue;

    public double Minimum => 0;

    public double Value => RatingControl.Value;

    public void SetValue(double value)
    {
        if (value < Minimum || value > Maximum)
        {
            throw new ArgumentOutOfRangeException(nameof(value));
        }

        RatingControl.Value = (int)value;
    }

    protected override AutomationControlType GetAutomationControlTypeCore()
    {
        return AutomationControlType.Slider;
    }

    protected override string GetClassNameCore()
    {
        return nameof(RatingControl);
    }
}

4. Common Automation Patterns

PatternPurposeExample Controls
IInvokeProviderSingle actionButton, MenuItem
IToggleProviderToggle stateCheckBox, ToggleButton
ISelectionProviderContains selectable itemsListBox, ComboBox
ISelectionItemProviderSelectable itemListBoxItem
IRangeValueProviderNumeric rangeSlider, ProgressBar
IValueProviderText valueTextBox
IExpandCollapseProviderExpand/collapseTreeViewItem, Expander
IScrollProviderScrollable contentScrollViewer

5. Raising Automation Events

5.1 Property Changed Event

public class CustomControlAutomationPeer : FrameworkElementAutomationPeer
{
    public void RaiseValueChanged(int oldValue, int newValue)
    {
        // Notify automation clients of value change
        RaisePropertyChangedEvent(
            RangeValuePatternIdentifiers.ValueProperty,
            (double)oldValue,
            (double)newValue);
    }

    public void RaiseSelectionChanged()
    {
        RaiseAutomationEvent(AutomationEvents.SelectionPatternOnInvalidated);
    }
}

5.2 From Control

public class RatingControl : Control
{
    private static void OnValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var control = (RatingControl)d;

        // Get automation peer and raise event
        var peer = UIElementAutomationPeer.FromElement(control) as RatingControlAutomationPeer;
        peer?.RaiseValueChanged((int)e.OldValue, (int)e.NewValue);
    }
}

6. Focus and Keyboard Navigation

6.1 Keyboard Support

public class RatingControl : Control
{
    public RatingControl()
    {
        // Enable keyboard focus
        Focusable = true;
        FocusVisualStyle = (Style)FindResource(SystemParameters.FocusVisualStyleKey);
    }

    protected override void OnKeyDown(KeyEventArgs e)
    {
        base.OnKeyDown(e);

        switch (e.Key)
        {
            case Key.Left:
            case Key.Down:
                if (Value > 0)
                {
                    Value--;
                    e.Handled = true;
                }
                break;

            case Key.Right:
            case Key.Up:
                if (Value < MaxValue)
                {
                    Value++;
                    e.Handled = true;
                }
                break;

            case Key.Home:
                Value = 0;
                e.Handled = true;
                break;

            case Key.End:
                Value = MaxValue;
                e.Handled = true;
                break;
        }
    }
}

6.2 Focus Visual

<Style TargetType="{x:Type local:RatingControl}">
    <Setter Property="FocusVisualStyle">
        <Setter.Value>
            <Style>
                <Setter Property="Control.Template">
                    <Setter.Value>
                        <ControlTemplate>
                            <Border BorderBrush="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"
                                    BorderThickness="2"
                                    CornerRadius="2"
                                    Margin="-2"/>
                        </ControlTemplate>
                    </Setter.Value>
                </Setter>
            </Style>
        </Setter.Value>
    </Setter>
</Style>

7. Screen Reader Announcements

7.1 Live Regions

<!-- Status updates announced when changed -->
<TextBlock x:Name="StatusText"
           AutomationProperties.LiveSetting="Polite"
           AutomationProperties.Name="Status"/>
// Update status - screen reader will announce
StatusText.Text = "3 items selected";

7.2 Programmatic Announcements

using System.Windows.Automation.Peers;

public static void Announce(string message)
{
    var peer = FrameworkElementAutomationPeer.FromElement(Application.Current.MainWindow);

    if (peer != null)
    {
        peer.RaiseAutomationEvent(AutomationEvents.LiveRegionChanged);
    }
}

8. Accessibility Checklist

Essential

  • All interactive elements have AutomationProperties.Name
  • Images have descriptive names or are marked decorative
  • Form fields are labeled (LabeledBy or Name)
  • Focus is visible and logical order is correct
  • Keyboard navigation works for all functionality

Enhanced

  • HelpText provides additional context
  • AcceleratorKey documents shortcuts
  • LiveSetting for dynamic content
  • Custom controls have AutomationPeers
  • Color contrast meets WCAG guidelines

9. Testing Accessibility

9.1 Using Inspect.exe

Tools location:
Windows SDK → bin → [arch] → inspect.exe

Usage:
1. Run inspect.exe
2. Hover over UI elements
3. View automation properties
4. Verify Name, ControlType, Patterns

9.2 Using Narrator

Windows + Ctrl + Enter: Toggle Narrator
Tab: Navigate forward
Shift + Tab: Navigate backward
Caps Lock + Up/Down: Read current item

10. References