microsoft/accessibility-insights-windows

Windows Automation: incorrect tree navigation for Table object.

gc-maximloverov opened this issue · 3 comments

Describe the bug
I have a UIA-grid object in which an ITableProvider is applied. The grid has 1 row and 3 columns (3 cells in total). Using the "Accessibility Insights for Windows" application I am trying to check each cell object by hovering over it with the mouse on a form. For cells in columns 2 and 3 everything works, but for cells in column 1 it does not work, why? The same problem exists in MS Power Automate app. I don't see this problem in the Inspector from the Windows SDK.

To Reproduce

  1. Run attached app:
    UiaTableProviderTest.zip
  2. Choose one of 4 startup projects (each of the projects meets the .net version).
  3. Run "Accessibility Insights for Windows" (AIW) app.
  4. Move the mouse pointer under each label, but not over the labels.

Expected behavior
In AIW app you can see each selected cell [0,0], [0,1], [0,2] corresponding to the UIA-object and its properties.

Actual behavior
In AIW app you can see each selected cell [0,1], [0,2] corresponding to the UIA-object and its properties, except cell [0,0].

Screenshots or .GIF
272623662-c07781d8-dcdd-438b-8932-3757436c56e9

Desktop (please complete the following information):

  • OS: Windows 10 19045.3448
  • Accessibility Insights for Windows Version: 1.1.2445.1
  • Target Application: attached app

Additional context
This issue is reproduced in the .Net Framework and .Net 6-8 using WinForms.

Columns 1 and 2 report an expected ancestry up to the desktop window. Column 0 does not. This lack of ancestry is causing AIWin to reject the element based on mouse hover. It can be selected through the hierarchy control.

All of this begs 2 questions:

  1. Why is the ancestry different?
  2. Should AIWin be able to select even with the different ancestry?

I'll update once I have more information

@gc-maximloverov, I figured out what is happening here. Your custom control is synthesizing a value for the RuntimeId property, but column 0 uses the same value as the custom row. The docs at https://learn.microsoft.com/en-us/dotnet/api/system.windows.automation.automationelement.runtimeidproperty?view=windowsdesktop-7.0 are explicit that each element needs to have an ID that is "unique on the desktop". Internally, Axe.Windows (the engine for Accessibility Insights) tries to walk the ancestry of the control, finds the duplicate RuntimeId property, and ends up ignoring the control. Inspect.exe from the SDK always walks the tree from the top down, so it never runs into this issue.

You can easily see the duplicate RuntimeId property by using the hierarchy control to select column 0 and opting to show all properties for the elements. On my machine, I see the following hierarchy:

pane 'Desktop 1'             RuntimeId of [2A,10010]
  windows 'Form1'            RuntimeId of [2A,20E0C]
    table 'GRID'             RuntimeId of [2A,50DFA]
      custom 'ROW 0'         RuntimeId of [2A,50DFA,4,0]
        item 'CELL (0,0)'    RuntimeId of [2A,50DFA,4,0]  <<< This duplicates its parent
        item 'CELL (0,1)'    RuntimeId of [2A,50DFA,4,1]
        item 'CELL (0,2)'    RuntimeId of [2A,50DFA,4,2]

The typical pattern here would be to add the column numbers as an additional value in the RuntimeId, so they are unique--something like this:

pane 'Desktop 1'             RuntimeId of [2A,10010]
  windows 'Form1'            RuntimeId of [2A,20E0C]
    table 'GRID'             RuntimeId of [2A,50DFA]
      custom 'ROW 0'         RuntimeId of [2A,50DFA,4,0]
        item 'CELL (0,0)'    RuntimeId of [2A,50DFA,4,0,0]  <<< This is now unique
        item 'CELL (0,1)'    RuntimeId of [2A,50DFA,4,0,1]
        item 'CELL (0,2)'    RuntimeId of [2A,50DFA,4,0,2]

Your code in CellUiaElement.GetRuntimeId() seems to be expecting to inherit the _rowIndex value that you append in RowUiaElemet.GetRuntimeId(), but that's not happening. I was able to make it work by modifying CellUiaElement.GetRuntimeId() to explicitly include the _rowIndex in its id, as follows:

public int[] GetRuntimeId()
{
    return new int[] { AutomationInteropProvider.AppendRuntimeId, _rowIndex, _columnIndex };
}

I'm closing this out since this is a bug in the data being passed into UIA.

Thanks for using Accessibility Insights!

@DaveTryon everything is clear now, thanks for the help!