如何让屏幕阅读器像阅读 Win32 MessageBox 一样阅读我的 WPF 消息?
How do I make screen readers read my WPF message similarly to how they read Win32 MessageBox?
我们有一个 WPF 桌面应用程序需要显示一些自定义消息 windows。我无法让屏幕 reader 正确朗读它们,例如 Freedom Scientific 的 JAWS。
我想实现与显示系统消息框时相同的行为。作为对比,System.Windows.MessageBox.Show("my message", "My Caption);
被 JAWS 宣布为 "My caption dialog. My message. OK Button"。这太完美了。
当我的消息 windows 打开时(仅包含 TextBlock
和 OK Button
),window 标题和 OK 按钮被宣布为有焦点,但 TextBlock
消息未公布。
这是一个显示问题的简单测试应用程序。当然,我们真正的应用程序有图标和其他状态文本。
<Window x:Class="Jaws_MessageBox_Test.MyMessageBox"
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:Jaws_MessageBox_Test"
mc:Ignorable="d"
Title="MyMessageBox" Height="300" Width="300">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"></RowDefinition>
<RowDefinition Height="Auto"></RowDefinition>
</Grid.RowDefinitions>
<TextBlock x:Name="mainLabel" Grid.Row="0">Hi there, this is a test to see if JAWS will read the main textbloc when shown.</TextBlock>
<Button Grid.Row="1" Margin="5" HorizontalAlignment="Right" Padding="10,0,10,0" IsDefault="True" x:Name="closeButton" Click="closeButton_Click">_Close</Button>
</Grid>
</Window>
当我使用以下方式显示时:
var mb = new MyMessageBox();
mb.ShowDialog();
屏幕 reader 宣布:"MyMessageBox. Close Button" 所以它不像系统消息框那样读取 TextBlock。
我使用 Windows SDK inspect
和 accevent
工具发现的是
系统消息框辅助功能类型为'Dialog',但WPF对话框的辅助功能类型为'Window'。这可能很重要。没有 UI 对话框的自动化控制类型 https://msdn.microsoft.com/en-us/library/ms749005(v=vs.110).aspx 。这可能是 WPF 中的错误或限制吗?
我已经尝试在我的 window 上设置各种 'AutomationProperties' 附加属性,这样 AutomationPeer
会有更好的信息,但其中 none在 ShowDialog
运行时阅读。
由于TextBlock
无法接收输入焦点,因此甚至无法通过 Tab 键读取文本。我暂时使用 read-only TextBox
来获得焦点,但体验仍然是错误的,我们的盲人用户不应该为了向他们阅读简单的状态消息而四处移动。
作为实验的一部分,我还尝试为消息 window 创建自己的派生 AutomationPeer
,但 Core
方法的 none启动对话框时会自动读取内容。自动化 child 列表确实将标题栏 object 列为第一个 child 而这是系统消息框的最后一个 child 尽管我看不到一种方法立即更改。
对于为盲人用户创建具有完整、适当的可访问性的 WPF-based 自定义消息框,我将不胜感激。
在文本块内的 运行 上设置 AutomationProperties.HelpText
例如:
<TextBlock>
<Run Text="aTextString" AutomationProperties.HelpText="ATextString"/>
</TextBlock>
或
<TextBlock>
<Run Text="aTextString" AutomationProperties.HelpText="{Binding Text, RelativeSource={RelativeSource self}}"/>
</TextBlock>
通过阅读可以确定问题是 Jaws 而不是 WPF,因为它往往不会读取标签和 TextBlocks 上的静态文本 - 奇怪的行为。
解决方法可能是使用文本框,设置 BorderStyle = None 并在其顶部放置一个矩形,填充 = 白色,不透明度 = 0.01。这将阻止用户将注意力集中在 TextBox 上,这意味着文本将不是静态的,Jaws 应该自动读取文本。 . .
有一点,是否必须由 Jaws 读取您的 App 弹出的对话框?
您是否考虑过在弹出对话框时使用 system.speech.synthesis.speechsynthesizer 朗读文本 - 只是一个想法!
您必须告诉自动化 API 您的 Window 是一个 MessageBox。
为此,请将此代码添加到您的 Window
protected override AutomationPeer OnCreateAutomationPeer()
{
return new MessageBoxWindowAutomationPeer(this);
}
并将此 class 添加到您的项目中
public class MessageBoxWindowAutomationPeer : WindowAutomationPeer
{
private const string WC_DIALOG = "#32770";
public MessageBoxWindowAutomationPeer(Window owner)
: base(owner)
{
}
protected override string GetClassNameCore()
{
return WC_DIALOG;
}
protected override string GetLocalizedControlTypeCore()
{
return "Dialogfeld";
}
protected override bool IsContentElementCore()
{
return true;
}
protected override bool IsControlElementCore()
{
return true;
}
}
因为我们的应用不需要本地化 "DialogFeld" 是德语本地化控件类型。本地化那个是你必须自己找出的部分。 ;-)
我不知道这是否是正确的解决方案,但这在 JAWS 18 上按要求工作。
<Window ...>
<UserControl>
<StackPanel>
<TextBlock Name="MessageText" ... />
<Button Name="OKButton" ...../>
</StackPanel>
</UserControl>
</Window>
然后在加载 window 时聚焦按钮。
所以我将 stackpanel 包裹在 usercontrol 元素中。
我们有一个 WPF 桌面应用程序需要显示一些自定义消息 windows。我无法让屏幕 reader 正确朗读它们,例如 Freedom Scientific 的 JAWS。
我想实现与显示系统消息框时相同的行为。作为对比,System.Windows.MessageBox.Show("my message", "My Caption);
被 JAWS 宣布为 "My caption dialog. My message. OK Button"。这太完美了。
当我的消息 windows 打开时(仅包含 TextBlock
和 OK Button
),window 标题和 OK 按钮被宣布为有焦点,但 TextBlock
消息未公布。
这是一个显示问题的简单测试应用程序。当然,我们真正的应用程序有图标和其他状态文本。
<Window x:Class="Jaws_MessageBox_Test.MyMessageBox"
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:Jaws_MessageBox_Test"
mc:Ignorable="d"
Title="MyMessageBox" Height="300" Width="300">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"></RowDefinition>
<RowDefinition Height="Auto"></RowDefinition>
</Grid.RowDefinitions>
<TextBlock x:Name="mainLabel" Grid.Row="0">Hi there, this is a test to see if JAWS will read the main textbloc when shown.</TextBlock>
<Button Grid.Row="1" Margin="5" HorizontalAlignment="Right" Padding="10,0,10,0" IsDefault="True" x:Name="closeButton" Click="closeButton_Click">_Close</Button>
</Grid>
</Window>
当我使用以下方式显示时:
var mb = new MyMessageBox();
mb.ShowDialog();
屏幕 reader 宣布:"MyMessageBox. Close Button" 所以它不像系统消息框那样读取 TextBlock。
我使用 Windows SDK inspect
和 accevent
工具发现的是
系统消息框辅助功能类型为'Dialog',但WPF对话框的辅助功能类型为'Window'。这可能很重要。没有 UI 对话框的自动化控制类型 https://msdn.microsoft.com/en-us/library/ms749005(v=vs.110).aspx 。这可能是 WPF 中的错误或限制吗?
我已经尝试在我的 window 上设置各种 'AutomationProperties' 附加属性,这样
AutomationPeer
会有更好的信息,但其中 none在ShowDialog
运行时阅读。由于
TextBlock
无法接收输入焦点,因此甚至无法通过 Tab 键读取文本。我暂时使用 read-onlyTextBox
来获得焦点,但体验仍然是错误的,我们的盲人用户不应该为了向他们阅读简单的状态消息而四处移动。作为实验的一部分,我还尝试为消息 window 创建自己的派生
AutomationPeer
,但Core
方法的 none启动对话框时会自动读取内容。自动化 child 列表确实将标题栏 object 列为第一个 child 而这是系统消息框的最后一个 child 尽管我看不到一种方法立即更改。
对于为盲人用户创建具有完整、适当的可访问性的 WPF-based 自定义消息框,我将不胜感激。
在文本块内的 运行 上设置 AutomationProperties.HelpText
例如:
<TextBlock>
<Run Text="aTextString" AutomationProperties.HelpText="ATextString"/>
</TextBlock>
或
<TextBlock>
<Run Text="aTextString" AutomationProperties.HelpText="{Binding Text, RelativeSource={RelativeSource self}}"/>
</TextBlock>
通过阅读可以确定问题是 Jaws 而不是 WPF,因为它往往不会读取标签和 TextBlocks 上的静态文本 - 奇怪的行为。
解决方法可能是使用文本框,设置 BorderStyle = None 并在其顶部放置一个矩形,填充 = 白色,不透明度 = 0.01。这将阻止用户将注意力集中在 TextBox 上,这意味着文本将不是静态的,Jaws 应该自动读取文本。 . .
有一点,是否必须由 Jaws 读取您的 App 弹出的对话框?
您是否考虑过在弹出对话框时使用 system.speech.synthesis.speechsynthesizer 朗读文本 - 只是一个想法!
您必须告诉自动化 API 您的 Window 是一个 MessageBox。 为此,请将此代码添加到您的 Window
protected override AutomationPeer OnCreateAutomationPeer()
{
return new MessageBoxWindowAutomationPeer(this);
}
并将此 class 添加到您的项目中
public class MessageBoxWindowAutomationPeer : WindowAutomationPeer
{
private const string WC_DIALOG = "#32770";
public MessageBoxWindowAutomationPeer(Window owner)
: base(owner)
{
}
protected override string GetClassNameCore()
{
return WC_DIALOG;
}
protected override string GetLocalizedControlTypeCore()
{
return "Dialogfeld";
}
protected override bool IsContentElementCore()
{
return true;
}
protected override bool IsControlElementCore()
{
return true;
}
}
因为我们的应用不需要本地化 "DialogFeld" 是德语本地化控件类型。本地化那个是你必须自己找出的部分。 ;-)
我不知道这是否是正确的解决方案,但这在 JAWS 18 上按要求工作。
<Window ...>
<UserControl>
<StackPanel>
<TextBlock Name="MessageText" ... />
<Button Name="OKButton" ...../>
</StackPanel>
</UserControl>
</Window>
然后在加载 window 时聚焦按钮。 所以我将 stackpanel 包裹在 usercontrol 元素中。