microsoft-ui-xaml icon indicating copy to clipboard operation
microsoft-ui-xaml copied to clipboard

[PipsPager Control] Narrator remains silent upon invoking "Next page/Previous page" button

Open zmkme opened this issue 7 months ago • 3 comments

Describe the bug

You can refer to bug link: https://microsoft.visualstudio.com/DefaultCollection/OS/_workitems/edit/56727518/

As is described in the title, When multiple click next/previous page in pipsPager, narrator will only read 'next/previous button' once at the first time, then remains silent.

This does not meet the requirements of A11Y.

Steps to reproduce the bug

https://github.com/user-attachments/assets/b3c14dcc-1141-41f6-950a-c757f1fa6989

This can be reproduced by any narrator

Expected behavior

No response

Screenshots

No response

NuGet package version

None

Windows version

No response

Additional context

No response

zmkme avatar May 23 '25 07:05 zmkme

We hope that each clicking, the narrator can read the "next/previous page button" once. And for the best, read the number of the currently displayed page.

A11Y Contact: [email protected]

Contact me: [email protected]

zmkme avatar May 23 '25 07:05 zmkme

Able to reproduce the issue. Will investigate further and provide a fix. @zmkme

sabareesanms avatar Jun 03 '25 09:06 sabareesanms

Hi @zmkme, you can work around this issue by fetching newly selected pips button from underneath ItemsRepeater and raise automation event using RaiseAutomationEvent API, you will also have to update the live settings of for pipbutton to Polite. See example below: pipPagerTest is the name of PipsPager control in MainWindow.xaml. You can subscribe to SelectedIndexChanged event and on callback, reach into visual tree and fetch ItemRepeater and from that you can get newly selected pipButton:

public MainWindow()
{
    InitializeComponent();
    pipsPagerTest.SelectedIndexChanged += PipsPagerTest_SelectedIndexChanged;
}

FrameworkElement GetChildFrameworkElementOfName(FrameworkElement current, string name)
{
    if (current.Name == name)
    {
        return current;
    }

    var count = VisualTreeHelper.GetChildrenCount(current);
    for (int i = 0; i < count; i++)
    {
        var child = VisualTreeHelper.GetChild(current, i) as FrameworkElement;
        FrameworkElement element = null;
        if (child != null)
            element = GetChildFrameworkElementOfName(child, name);
        if (element != null)
            return element;
    }

    return null;
}

private void PipsPagerTest_SelectedIndexChanged(PipsPager sender, PipsPagerSelectedIndexChangedEventArgs args)
{
    if (sender != null)
    {
        var itemsRepeater = GetChildFrameworkElementOfName(sender, "PipsPagerItemsRepeater") as ItemsRepeater;
        if (itemsRepeater != null)
        {
            var pipUIElement = itemsRepeater.GetOrCreateElement(sender.SelectedPageIndex);
            if (pipUIElement != null)
            {
                var pipButton = pipUIElement as Button;
                if (pipButton != null)
                {
                    AutomationProperties.SetLiveSetting(pipButton, AutomationLiveSetting.Polite);
                    var pipButtonPeer = FrameworkElementAutomationPeer.FromElement(pipButton);
                    pipButtonPeer.RaiseAutomationEvent(AutomationEvents.LiveRegionChanged);
                }
            }
        }
    }
}

There is a bit redundancy here as we may end up updating live setting multiple times for a button, but this can be used as a work around for now.

Hemantxk avatar Jun 11 '25 10:06 Hemantxk