ControlAccessibleObject should bridge GetFocused() to UIA IRawElementProviderFragmentRoot.GetFocus()
- Dominant language
- C#
- Stars
- 4.9k
- Forks
- 1.1k
- Avg merge
- 20h 23m
- Merged PRs (30d)
- 103
Description
## Summary
When building a custom WinForms control with child accessible objects (e.g. a grid where each cell is a `CellAccessibleObject : AccessibleObject`), overriding `GetFocused()` on a `ControlAccessibleObject` subclass correctly exposes the focused child to MSAA clients. However, UIA clients, including NVDA, never see it.
## Root cause
`IRawElementProviderFragmentRoot.GetFocus()` is implemented in `AccessibleObject` by calling an `internal virtual` method that returns `null` by default. `ControlAccessibleObject` does not override it, so UIA always reports the control itself as focused, regardless of what `GetFocused()` returns. `DataGridView` works correctly because it overrides `GetFocus()` from *inside* the WinForms assembly:
```csharp
internal override IRawElementProviderFragment.Interface? GetFocus() => GetFocused();
```
External code has no equivalent escape hatch. GetFocused() is public and functional, but the UIA path never reaches it. To repro:
1. Create a custom control with a ControlAccessibleObject subclass
2. Override GetFocused() to return a child AccessibleObject
3. Focus the control with NVDA running
4. Press NVDA+Tab to read the focused element
Actual result: NVDA reports the control, not the child cell.
Expected result: NVDA reports the focused child (the cell accessible object returned by GetFocused())
## Proposed fix
Either make GetFocus() protected virtual so external subclasses can override it, or have ControlAccessibleObject implement it as return GetFocused() as IRawElementProviderFragment.Interface; by default, matching what DataGridView already does internally.
Contributor guide
Assessment
This issue has not been assessed yet.