当列表视图(WPF)的项目发生变化时,如何强制屏幕阅读器(JAWS)宣布自定义文本?

时间:2013-07-09 06:48:52

标签: wpf screen-readers jaws-screen-reader

我有一个需要支持屏幕阅读器(尤其是JAWS)的WPF应用程序。问题是,当列表视图项已更改(添加,删除)时,JAWS不会发布任何内容。盲人用户完全不知道发生了什么。我有什么办法强制屏幕阅读器宣布一些文本,当尝试从列表视图控件添加/删除项目?我该怎么办?

3 个答案:

答案 0 :(得分:3)

如果JAWS读者不支持此功能,您可以通过SpeechSynthesizer自行实施。语音播放示例:

using System.Speech.Synthesis;

SpeechSynthesizer MySpeechSynthesizer = new SpeechSynthesizer();
MySpeechSynthesizer.Speak("Hello!");

我使用了ObservableCollection分配ListBox的示例。 ObservableCollection是一个事件CollectionChanged,其中包含对集合 [MSDN]执行的行为的枚举:

Member name   Description
------------  ------------
Add           One or more items were added to the collection.
Move          One or more items were moved within the collection.
Remove        One or more items were removed from the collection.
Replace       One or more items were replaced in the collection.
Reset         The content of the collection changed dramatically.

event将按如下方式实施:

// Set the ItemsSource
SampleListBox.ItemsSource = SomeListBoxCollection;

// Set handler on the collection
SomeListBoxCollection.CollectionChanged += new NotifyCollectionChangedEventHandler(SomeListBoxCollection_CollectionChanged);

private void SomeListBoxCollection_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
    if (e.Action == NotifyCollectionChangedAction.Add)
    {
        // Some actions, in our case - speech
    }
}

以下是我的例子:

XAML

<Window x:Class="JAWShelp.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="MainWindow" Height="350" Width="525"
    WindowStartupLocation="CenterScreen">

    <Grid>
        <ListBox Name="MyListBox" DisplayMemberPath="Name" SelectedIndex="0" Width="100" Height="100" Loaded="MyListBox_Loaded" />

        <WrapPanel Width="200" Height="30" Margin="40,150,0,0">
            <Button Name="AddButton" Padding="5" Content="Add item" VerticalAlignment="Bottom" Click="AddButton_Click" />
            <Button Name="RemoveButton" Padding="5" Margin="30,0,0,0" Content="Remove item" VerticalAlignment="Bottom" Click="RemoveButton_Click" />
        </WrapPanel>
    </Grid>
</Window>

Code behind

// using System.Speech.Synthesis;
// using System.Collections.ObjectModel;
// using System.Collections.Specialized;

public partial class MainWindow : Window
{
    public class Person
    {
        public string Name
        {
            get;
            set;
        }
    }

    private ObservableCollection<Person> DataForListBox = new ObservableCollection<Person>();

    public MainWindow()
    {
        InitializeComponent();
    }

    private void MyListBox_Loaded(object sender, RoutedEventArgs e)
    {
        DataForListBox.Add(new Person()
        {
            Name = "Peter Orange",                
        });

        MyListBox.ItemsSource = DataForListBox;

        DataForListBox.CollectionChanged += new NotifyCollectionChangedEventHandler(DataForListBox_CollectionChanged);
    }

    private void DataForListBox_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
    {
        if (e.Action == NotifyCollectionChangedAction.Add)
        {
            SpeechSynthesizer MySpeechSynthesizer = new SpeechSynthesizer();

            MySpeechSynthesizer.Speak("You are add item.");
        }

        if (e.Action == NotifyCollectionChangedAction.Remove)
        {
            SpeechSynthesizer MySpeechSynthesizer = new SpeechSynthesizer();

            MySpeechSynthesizer.Speak("You are remove item.");
        }
    }

    private void AddButton_Click(object sender, RoutedEventArgs e)
    {
        DataForListBox.Add(new Person()
        {
            Name = "Jack Rider",
        });
    }

    private void RemoveButton_Click(object sender, RoutedEventArgs e)
    {
        DataForListBox.RemoveAt(1);
    }
}

没有问题,您可以添加Add/Remove项目的复制文本。您还可以使用PromptBuilder添加播放.wav文件:

PromptBuilder MyPromptBuilder = new PromptBuilder();

MyPromptBuilder.AppendAudio("SomeFile.wav");

答案 1 :(得分:3)

JAWS只会响应获得焦点的控件。我在我的应用程序中需要类似的功能,并使用以下内容解决它。

  1. 在布局中添加两个隐藏的文本框控件。

    <!--Controls used to announce accessibility messages for screen readers.-->
    <TextBox x:Name="ATMessage_Silent"  Height="1" Width="1" IsTabStop="False" AutomationProperties.Name=" "/>
    <TextBox x:Name="ATMessage_Audible" Height="1" Width="1" IsTabStop="False"/>
    
  2. 添加课程以宣布消息。我发现为了使它可靠,我需要在多个控件之间传递焦点之间暂停。否则JAWS无法可靠地宣布消息。

    public class AccessibilityMessage
    {
        private AccessibilityMessage(object sender, string message, double delay)
        {
            DispatcherTimer sleep = new DispatcherTimer();
            int counter = 3;
    
            try
            {   
                if (accessibilityMessageAudibleControl != null && accessibilityMessageSilentControl != null)
                {
                    sleep.Interval = TimeSpan.FromMilliseconds(delay);
    
                    // Update the message.
                    accessibilityMessageAudibleControl.SetValue(AutomationProperties.NameProperty, message);
    
                    // Give focus to the silent control.
                    accessibilityMessageSilentControl.IsTabStop = true;
                    accessibilityMessageSilentControl.Focus();
    
                    // Give focus to the message.
                    accessibilityMessageAudibleControl.IsTabStop = true;
                    accessibilityMessageAudibleControl.Focus();
    
                    // Use a timer to simulate a sleep. We need to pause briefly to give enough time
                    // for the screen reader to process the focus on the message control. After a brief
                    // pause we will give focus back to the original control. If we do not pause like
                    // this the screen reader will not reliably react to the message.
                    sleep.Tick += (s, e) =>
                    {
                        counter--;
    
                        // Check to see if it is time to focus the original control.
                        if (counter == 0)
                        {
                            // Return focus to the original control that triggered the message.
                            if (sender != null && sender is Control)
                            {
                                // Give focus back to the original control.
                                ((Control)sender).Focus();
                            }
    
                            // Exit the timer.
                            sleep.Stop();
    
                            // Inform any listeners the message has been announced.
                            if (Announced != null)
                                Announced(this, null);
                        }
                    };
    
                    // Start the time.
                    sleep.Start();
                }
                else
                {
                    throw new Exception("Accessibility message controls are not defined in the Application Manager. Unable to announce accessibility message.");
                }
            }
            catch (Exception ex)
            {
                ErrorDialog.Show(ex, sender);
            }
        }
    
        public event EventHandler Announced;
    
        public static AccessibilityMessage Announce(object sender, string message, double delay = 250)
        {
            return new AccessibilityMessage(sender, message, delay);
        }
    }
    
  3. 宣布您的消息。您可以简单地发布公告或使用宣布的事件发布公告,然后在公告发布后再执行其他工作。

    发布通知,告知用户在数据网格加载数据时请等待。

    // Pass myGrid as the sender so it will receive focus after the announcement.
    ApplicationManager.AccessibilityMessage.Announce(myGrid, "Loading purchase orders table, please wait.").Announced += (s, arg) =>
    {
        // MAKE WEB SERVICE CALL TO RETRIEVE DATA.
        DataService svc = new DataService();
        svc.ListPurchasOrdersCompleted += OnListPurchaseOrders_Completed();
        svc.ListPurchaseOrders();
     };
    

    宣布数据已加载到数据网格中。

    private void OnListPurchaseOrders_Completed(object sender, AsyncCompletedEventArgs e)
    {
        try
        {
            if (e.Error == null)
            {
                myGrid.ItemsSource = e.Result();
    
                // Pass myGrid as the sender so it will receive focus after the announcement.
                AccessibilityMessage.Announce(myGrid, string.Format("Loaded {0} orders into the purchase orders table.", myGrid.Items.Count));
            }
            else
            {
                throw e.Error;
            }
        }
        catch (Exception ex)
        {
            ErrorDialog.Show(ex, this);
        }
    }
    
  4. 使用此功能,您只需使用Announce()调用即可随时发布通知。我最初是为Silverlight实现的。它也适用于WPF。

答案 2 :(得分:0)

我喜欢的方式是在用户界面中使用高度为0的TextBlock:

10
20
30
40
50
60
70
80
90
01
11
21
31
41
51
61
71
81
91
02
12
22
32
42
52
62
72
82
92
03
13
23
33
43
53
63
73
83
93
04
14
24
34
44
54
64
74
84
94
05
15
25
35
45
55
65
75
85
95
06
16
26
36
46
56
66
76
86
96
07
17
27
37
47
57
67
77
87
97
08
18
28
38
48
58
68
78
88
98
09
19
29
39
49
59
69
79
89
99

然后我使用此方法使它读取一些内容:

<TextBlock
    x:Name="screenReaderText"
    Height="0"
    AutomationProperties.LiveSetting="Assertive" />

基本上是说:“看,此控件更改了内容,最好将其读出。”