在WPF应用程序中,如何使用屏幕阅读器在标签中动态读取变化的值?

在WPF应用程序中,如何使用屏幕阅读器在标签中动态读取变化的值?,wpf,visual-studio-2017,accessibility,ui-automation,Wpf,Visual Studio 2017,Accessibility,Ui Automation,我有一个WPF应用程序,它打印1到50个数字,下面给出了XAML和代码。我的要求是每次设置新内容时,使用屏幕阅读器NVDA读取标签值。我的问题是如何实现动态变化的内容的读出?你能帮我做到这一点吗?谢谢 我的XAML是 <Window x:Class="WPFAccessibility.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="h

我有一个WPF应用程序,它打印1到50个数字,下面给出了XAML和代码。我的要求是每次设置新内容时,使用屏幕阅读器NVDA读取标签值。我的问题是如何实现动态变化的内容的读出?你能帮我做到这一点吗?谢谢

我的XAML是

<Window x:Class="WPFAccessibility.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WPFAccessibility"
        mc:Ignorable="d"
        Title="WPFAccessibility" Height="450" Width="800">
    <Grid>
        <Label Name="progressLabel" FontSize="20" Margin="50,50"></Label>
    </Grid>
</Window>

我的代码隐藏文件是

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Threading;

namespace WPFAccessibility
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();

            var mySource = Enumerable.Range(1, 50).ToList();
            Task.Factory.StartNew(() => DoOperation(mySource));
        }

        private void DoOperation(List<int> values)
        {
            foreach (var i in values)
            {
                Thread.Sleep(1000);
                var currentProgress = i;
                Dispatcher.BeginInvoke(new Action(() =>
                {
                    Process(currentProgress);

                }), DispatcherPriority.Background);
            }
        }

        private void Process(int currentProgress)
        {
            progressLabel.Content = "Processing...   " + currentProgress;

            if (currentProgress == 50)
                progressLabel.Content = "Processing completed.";
        }
    }
}
使用系统;
使用System.Collections.Generic;
使用System.Linq;
使用系统线程;
使用System.Threading.Tasks;
使用System.Windows;
使用System.Windows.Threading;
命名空间可访问性
{
/// 
///MainWindow.xaml的交互逻辑
/// 
公共部分类主窗口:窗口
{
公共主窗口()
{
初始化组件();
var mySource=Enumerable.Range(1,50).ToList();
Task.Factory.StartNew(()=>DoOperation(mySource));
}
私有void操作(列表值)
{
foreach(值中的var i)
{
睡眠(1000);
var currentProgress=i;
Dispatcher.BeginInvoke(新操作(()=>
{
过程(当前进度);
}),DispatcherPriority.Background);
}
}
私有无效进程(int currentProgress)
{
progressLabel.Content=“处理…”+当前进度;
如果(当前进度==50)
progressLabel.Content=“处理已完成。”;
}
}
}

我可能应该将此作为对您问题的评论,但我认为它可能会帮助您找到正确的答案,因此我想将其作为答案发布。我理解可访问性,因为它与web应用程序有关,但我没有WPF应用程序的经验,但这种相似性可能会有所帮助

使用html,您可以对元素使用属性,这样当元素中的文本发生更改时,更改将被宣布。您可以控制是只宣布更改的文本的一小部分,还是宣布整个元素

例如:

<span aria-live="true">You have <span id="timeleft">X</span> seconds left</span>
现在,当X变为5时,屏幕阅读器会说“您还有5秒钟”。读取整个
元素

那么,这与你最初的问题有什么关系呢?您应该能够使用自动化属性来执行类似的操作。特别是
LiveSetting
属性。尽管这个博客已经有一年了(2017年9月),但它有一些关于
LiveSetting
属性的好信息。

我在标签控件和代码部分使用了AutomationProperties.LiveSetting=“little”,我创建了AutomationPeer来向UIA引发PropertyChanged事件。此LiveSettings功能仅适用于.NET framework 4.7.1及更高版本

XMAL

<Window x:Class="WPFAccessibility.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WPFAccessibility"
        mc:Ignorable="d"
        Title="WPFAccessibility" Height="450" Width="800">
    <Grid>
        <Label Name="progressLabel" FontSize="20" Margin="50,50" AutomationProperties.LiveSetting="Polite"></Label>
    </Grid>
</Window>

代码

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Threading;
namespace WPFAccessibility
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        private static int counter = 0;
        public MainWindow()
        {
            InitializeComponent();

            var mySource = Enumerable.Range(1, 50).ToList();
            Task.Factory.StartNew(() => DoOperation(mySource));
        }

        private void DoOperation(List<int> values)
        {
            foreach (var i in values)
            {
                Thread.Sleep(2000);
                var currentProgress = i;
                Dispatcher.BeginInvoke(new Action(() =>
                {
                    Process(currentProgress);

                }), DispatcherPriority.Background);
            }
        }

        private void Process(int currentProgress)
        {
            progressLabel.Content = "Processing...   " + currentProgress;

            if (currentProgress == 50)
                progressLabel.Content = "Processing completed.";

            var peer = UIElementAutomationPeer.FromElement(progressLabel);
            if (peer == null)
                peer = UIElementAutomationPeer.CreatePeerForElement(progressLabel);
            peer.RaiseAutomationEvent(AutomationEvents.LiveRegionChanged);  
        }       
    }
}
使用系统;
使用System.Collections.Generic;
使用System.Linq;
使用系统线程;
使用System.Threading.Tasks;
使用System.Windows;
使用System.Windows.Threading;
命名空间可访问性
{
/// 
///MainWindow.xaml的交互逻辑
/// 
公共部分类主窗口:窗口
{
专用静态整数计数器=0;
公共主窗口()
{
初始化组件();
var mySource=Enumerable.Range(1,50).ToList();
Task.Factory.StartNew(()=>DoOperation(mySource));
}
私有void操作(列表值)
{
foreach(值中的var i)
{
《睡眠》(2000年);
var currentProgress=i;
Dispatcher.BeginInvoke(新操作(()=>
{
过程(当前进度);
}),DispatcherPriority.Background);
}
}
私有无效进程(int currentProgress)
{
progressLabel.Content=“处理…”+当前进度;
如果(当前进度==50)
progressLabel.Content=“处理已完成。”;
var peer=UIElementAutomationPeer.FromElement(progressLabel);
if(peer==null)
peer=UIElementAutomationPeer.CreatePeerForElement(progressLabel);
peer.RaiseAutomationEvent(AutomationEvents.LiveRegionChanged);
}       
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Threading;
namespace WPFAccessibility
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        private static int counter = 0;
        public MainWindow()
        {
            InitializeComponent();

            var mySource = Enumerable.Range(1, 50).ToList();
            Task.Factory.StartNew(() => DoOperation(mySource));
        }

        private void DoOperation(List<int> values)
        {
            foreach (var i in values)
            {
                Thread.Sleep(2000);
                var currentProgress = i;
                Dispatcher.BeginInvoke(new Action(() =>
                {
                    Process(currentProgress);

                }), DispatcherPriority.Background);
            }
        }

        private void Process(int currentProgress)
        {
            progressLabel.Content = "Processing...   " + currentProgress;

            if (currentProgress == 50)
                progressLabel.Content = "Processing completed.";

            var peer = UIElementAutomationPeer.FromElement(progressLabel);
            if (peer == null)
                peer = UIElementAutomationPeer.CreatePeerForElement(progressLabel);
            peer.RaiseAutomationEvent(AutomationEvents.LiveRegionChanged);  
        }       
    }
}