Mam aplikację WPF, która wymaga obsługi czytnika ekranu (zwłaszcza JAWS). Problem polega na tym, że JAWS nie ogłasza niczego, gdy elementy widoku listy zostały zmienione (dodane, usunięte). I niewidomi użytkownicy zupełnie nie wiedzą, co się stało. Czy istnieje jakikolwiek sposób, aby czytnik ekranu ogłosił jakiś tekst podczas próby dodania/usunięcia elementu z kontrolki widoku listy? i Jak mogę to zrobić?Jak zmusić czytnik ekranu (JAWS) do ogłoszenia niestandardowego tekstu, gdy elementy widoku listy (WPF) zostały zmienione?
Odpowiedz
Jeśli czytnik JAWS
nie obsługuje tej funkcji, można ją zaimplementować samodzielnie przez SpeechSynthesizer
. Przykład odtwarzania głos
using System.Speech.Synthesis;
SpeechSynthesizer MySpeechSynthesizer = new SpeechSynthesizer();
MySpeechSynthesizer.Speak("Hello!");
I używana przykład ObservableCollection
przypisanego ListBox
. ObservableCollection
jest wydarzeniem CollectionChanged
, w który zawiera wyliczenie czynności wykonywanych na gromadzeniu [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.
Ten event
będą realizowane tak:
// 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
}
}
Poniżej jest mój przykład:
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);
}
}
Bez problemów można dodać tekst reprodukcji Add/Remove
elementu. Można również dodać play .wav
plik używając PromptBuilder
:
PromptBuilder MyPromptBuilder = new PromptBuilder();
MyPromptBuilder.AppendAudio("SomeFile.wav");
Dzięki. ale chciałbym, żeby JAWS ogłosił tekst. –
JAWS będzie reagować tylko do kontroli, które zyskują ostrość. Potrzebowałem podobnej funkcjonalności w mojej aplikacji i rozwiązałem ją następująco.
Dodaj do układu dwa ukryte elementy sterujące pola tekstowego.
<!--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"/>
Dodaj zajęcia, aby ogłosić wiadomości. Zauważyłem, że aby był niezawodny, musiałem krótko przerwać między przekazywaniem fokusu pomiędzy wieloma kontrolami. W przeciwnym razie JAWS nie będzie rzetelnie informować o wiadomościach.
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); } }
Ogłoś swoje wiadomości. Możesz albo po prostu wydać ogłoszenie, albo skorzystać z ogłoszonego wydarzenia, możesz zrobić ogłoszenie, a następnie wykonać dodatkową pracę po ogłoszeniu.
Wprowadź komunikat, aby poinformować użytkownika, że należy poczekać na załadowanie danych do siatki danych.
// 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(); };
Wprowadź komunikat, że dane zostały załadowane do siatki danych.
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); } }
Korzystanie z tego można zrobić zapowiedzi kiedy chcesz po prostu za pomocą ogłaszają() wywołanie. Pierwotnie zaimplementowałem to dla Silverlight. Powinno to również działać w przypadku WPF.
Dzięki Eric, działa dobrze dla mnie. –
- 1. Jak grupować elementy w widoku listy WPF
- 2. C#: Jak wykryć, czy działa czytnik ekranu?
- 3. Czytnik ekranu JAWS dodaje tabIndex od -1 do kotwicy ze zdjęciami
- 4. Jak zmusić VoiceOver do ogłoszenia etykiet sekcji w systemie iOS?
- 5. Jak otoczyć zawartość w widoku listy WPF?
- 6. Jak zmusić Aurelia do renderowania widoku po dynamicznym dodawaniu niestandardowego elementu do DOM?
- 7. Jak uzyskać wybrane elementy listy z widoku listy przy pomocy checkBox i niestandardowego adaptera?
- 8. QML, dynamicznie dodając elementy do widoku listy
- 9. Zatrzymaj Hibernate z aktualizacją kolekcji, gdy nie zostały zmienione
- 10. Przechodzenie z widoku listy do widoku siatki
- 11. Ukryj kolumnę widoku listy wpf
- 12. Jak zmusić QGLWidget do aktualizacji ekranu?
- 13. Czy czytnik ekranu może być "zablokowany" w elemencie DIV?
- 14. Pokaż dane niestandardowego obiektu do ListBox WPF
- 15. Jak ukryć nagłówek WPF widoku listy?
- 16. Jak usunąć elementy z widoku listy JQUERY mobile
- 17. jak zmusić fokus do edycji tekstu
- 18. WPF Binding IsSelected do ViewModel nie określa elementy, które nie zostały pokazane na liście
- 19. Wyświetlanie ogłoszenia u dołu bez nakładania się na widok listy
- 20. WPF Jak zmienić kolor tekstu wybranego pola listy, gdy pole listy traci ostrość
- 21. Jak zaktualizować tylko atrybuty, które zostały zmienione - Spring MVC
- 22. Dodawanie widoku niestandardowego do UINavigationController
- 23. Jak zmusić fokus do zmiany widoku w tvOS?
- 24. Jak zmusić obiekt listy do typu „double”
- 25. Dlaczego Chrome ma problemy z czytnikiem ekranu JAWS?
- 26. Czy można włączyć płynne przewijanie w widoku listy WPF?
- 27. Android - czytnik epub do odczytu plików .epub ..
- 28. Jak zmusić IntelliJ IDEA do ponownego wyświetlenia ekranu powitalnego?
- 29. wstawianie niestandardowego tekstu do ggplot2
- 30. Jak przekazać atrybut AttributeSet do niestandardowego widoku
Możesz spróbować ustawić ostrość nowego elementu. Zazwyczaj narzędzia narrator/accessibility tylko czytają, co jest skupione. – AndrewS