2

We have a WPF desktop application which needs to show some custom message windows. I am having trouble getting them to be read aloud properly by screen readers such as JAWS from Freedom Scientific.

I want to achieve the same behavior as when showing a system message box. For comparison, System.Windows.MessageBox.Show("my message", "My Caption); is announced by JAWS as "My caption dialog. My message. OK Button". This is perfect.

When my message windows are opened (containing only a TextBlock and OK Button), the window title is announced and the OK button is announced as having focus but the TextBlock message is not announced.

Here's a simple test application which shows the issue. Our real app has icons and other status text, of course.

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

When I show this using:

var mb = new MyMessageBox();
mb.ShowDialog();

The screen reader announces: "MyMessageBox. Close Button" so it's not reading the TextBlock like the system message box does.

What I've found using the Windows SDK inspect and accevent tools is that

  • The system message box accessibility type is 'Dialog' but the WPF dialog's accessibility type is 'Window'. This might matter. There is no UI Automation Control Type of Dialog https://msdn.microsoft.com/en-us/library/ms749005(v=vs.110).aspx . Is this a bug or limitation in WPF perhaps?

  • I have tried setting various 'AutomationProperties' attached properties on my window so that the AutomationPeer will have better info but none of those are read when the ShowDialog runs.

  • Since TextBlock cannot receive input focus, there's no way to even get the text read by tabbing. I temporarily use a read-only TextBox instead to get focus but the experience is still wrong and our blind users should not have to tab around just to have a simple status message read to them.

  • As part of the experimenting, I also tried creating my own derived AutomationPeer for the message window but none of the Core method content is read automatically when the dialog is launched. The automation child list does have the title bar object listed as the first child whereas that's the last child for the system message box though I don't see a way to change that right now.

I'd greatly appreciate any help for creating a WPF-based custommessage box with full, proper accessibility for blind users.

jschroedl
  • 4,916
  • 3
  • 31
  • 46

5 Answers5

4

You have to tell the automation API that your Window is a MessageBox. To do that add this code to your Window

protected override AutomationPeer OnCreateAutomationPeer()
{
    return new MessageBoxWindowAutomationPeer(this);
}

and add this class to your project

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

As we don't need localization in our app "DialogFeld" is the german localized control type. Localizing that one is the part you would have to find out by yourself. ;-)

batzen
  • 161
  • 2
  • 4
  • Interesting! I had not thought to use that for a class name. Trying this out, it doesn't automatically read the first item like it does with OS message boxes. Does it read them for you? Perhaps I need a change to my TextBlock too. – jschroedl Jan 11 '17 at 14:15
  • I only tried it with NVDA and it works there, there was no need to change anything in your dialog xaml. Will test it with JAWS and report my findings. This will take a few weeks as i am very busy right now. – batzen Jan 20 '17 at 09:54
  • I tried a lot, but i was unable to find out what needs to be done to get JAWS to read the content of the dialog. Maybe you should ask the company behind JAWS how they detect dialogs. I had to uninstall JAWS because it causes massive slowdowns on my machine. – batzen Mar 12 '17 at 07:56
  • I had to uninstall JAWS as well - you're right it really messes with your system. For future visitors, I tried contacting JAWS support but they only had advice for people trying to use the JAWS product and no advice for developers. The support contact was even surprisingly a bit belligerent toward my questions. – jschroedl Dec 20 '18 at 14:39
1

Set the AutomationProperties.HelpText on the run inside the Textblock

So for Example:

<TextBlock>
    <Run Text="aTextString" AutomationProperties.HelpText="ATextString"/>
</TextBlock>

or

<TextBlock>
    <Run Text="aTextString" AutomationProperties.HelpText="{Binding Text, RelativeSource={RelativeSource self}}"/>
</TextBlock>
DaveM121
  • 46
  • 2
  • I've tried this and just tried again to be sure. Sadly, the text of the HelpText is not read when the dialog is shown. How I wish it were. It only reads the window title and then the text of the focus object (Close button). There seems to be something I'd need to set on the window to tell readers that this is a dialog and there's interesting text to read before reading the text of the focus object. – jschroedl Jan 06 '17 at 18:00
0

OK from reading around the problem is with Jaws not WPF, as it tends not to read static text on Labels and TextBlocks - strange behaviour.

A workaround might be to use a TextBox, set the BorderStyle = None and place a rectangle on top of it, with fill = White, Opacity = 0.01. This will stop the user being able to focus on the TextBox and means the text will not be static and Jaws should read the text automatically . . .

DaveM121
  • 46
  • 2
  • I'm currently using a ReadOnly TextBox similar to what you describe. I am beginning to think that this is a WPF limitation as there doesn't seem to be a way to identify a WPF-based window as a 'Dialog' to screen readers. If I could, I think it would seek out the initial label.... or perhaps it triggers an accessibility event which causes the text to be read. So frustrating... – jschroedl Jan 08 '17 at 18:17
0

One thing, does it have to be Jaws that reads the Dialogs that your App pops up?

Have you looked at using system.speech.synthesis.speechsynthesizer to speak the text when the dialog pops up - just a thought!

DaveM121
  • 46
  • 2
  • Our customers use Jaws so I'm investigating why our application does not have the messages read but others do. The others are Win32 apps, not WPF as ours is which I think is the problem. – jschroedl Jan 08 '17 at 18:15
0

I don't know if this is correct solution but this works as required on JAWS 18.

<Window ...>
<UserControl>
    <StackPanel>
        <TextBlock Name="MessageText" ... />
        <Button Name="OKButton" ...../>
    </StackPanel>
</UserControl>
</Window>

and then focusing the button when window is loaded. So I wrapped stackpanel inside the usercontrol element.

shubham pagui
  • 145
  • 1
  • 6