2013-07-09 16 views
7

Ho un'applicazione WPF che necessita di screen reader di supporto (in particolare JAWS). Il problema è che JAWS non annuncia nulla, quando gli elementi della vista elenco sono cambiati (aggiunti, rimossi). E gli utenti non vedenti non sanno assolutamente cosa sia successo. È possibile forzare lo screen reader ad annunciare del testo quando si tenta di aggiungere/rimuovere elementi dal controllo della visualizzazione elenco? e come posso farlo?Come posso forzare lo screen reader (JAWS) ad annunciare un testo personalizzato, quando sono cambiate le voci della visualizzazione elenco (WPF)?

+0

si potrebbe provare a concentrarsi il nuovo elemento. In genere gli strumenti narratore/accessibilità leggono solo ciò che è focalizzato. – AndrewS

risposta

3

Se il lettore JAWS non supporta questa funzione, è possibile implementarlo da solo SpeechSynthesizer. Esempio la riproduzione vocale:

using System.Speech.Synthesis; 

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

ho usato l'esempio di un ObservableCollection che viene assegnato ListBox. ObservableCollection è un evento CollectionChanged, in quanto contiene l'enumerazione di atti compiuti sulla raccolta [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. 

Questo event sarà attuato in questo modo:

// 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 
    } 
} 

Di seguito è riportato il mio esempio:

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); 
    } 
} 

senza problemi, è possibile aggiungere il testo riproduzione di Add/Remove voce. È inoltre possibile aggiungere il gioco .wav file usando PromptBuilder:

PromptBuilder MyPromptBuilder = new PromptBuilder(); 

MyPromptBuilder.AppendAudio("SomeFile.wav"); 
+0

Grazie. ma vorrei che JAWS annunci il testo. –

3

JAWS risponderà solo ai controlli che guadagno messa a fuoco. Avevo bisogno di funzionalità simili nella mia applicazione e l'ho risolto con il seguente.

  1. Aggiungere due controlli di casella di testo nascosti al layout.

    <!--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. Aggiungere una classe per annunciare i messaggi. Ho trovato che per renderlo affidabile avevo bisogno di fare una pausa breve tra il passaggio del focus tra più controlli. Altrimenti, JAWS non annuncia in modo affidabile i messaggi.

    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. Annuncia i tuoi messaggi. Puoi semplicemente fare un annuncio o utilizzare l'evento Annunciato, puoi fare l'annuncio e poi eseguire ulteriori lavori dopo l'annuncio.

    Fare un annuncio per informare l'utente di attendere mentre una griglia di dati viene caricata con i dati.

    // 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(); 
    }; 
    

    Fare l'annuncio che i dati sono stati caricati nella griglia di dati.

    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); 
        } 
    } 
    

Usando questo si può fare annunci quando vuoi semplicemente utilizzando la chiamata Annunciare(). Inizialmente l'ho implementato per Silverlight. Dovrebbe funzionare anche per WPF.

+0

Grazie Eric, sta funzionando bene per me. –