Accessible Data: Making your data available for all

Making sure your application is accessible for tools like screen readers is important and you should be aware of and follow the guidelines outlined in Accessibility Best Practices.

 

One place you should be mindful about accessibility is when you bind a control to data. How you make your data accessible depends on how you store your data. Consider the following example, which binds a ListBox to an ObservableCollection:

 

    public class Employee

    {

        string name;

        string type;

        string number;

        public Employee(string newName, string newType, string newNumber)

        {

            name = newName;

            type = newType;

            number = newNumber;

        }

        public string EmployeeName

        {

            set { name = value; }

            get { return name; }

        }

        public string Type

        {

            set { type = value; }

            get { return type; }

        }

        public string EmployeeNumber

        {

            set { number = value; }

            get { return number; }

        }

        public override string ToString()

        {

            return EmployeeName;

        }

    }

    public class EmployeeList : ObservableCollection<Employee>

    {

        public EmployeeList()

        {

            this.Add(new Employee("Terry Adams", "FTE", "1"));

            this.Add(new Employee("Claire O'Donnell", "FTE", "12345"));

            this.Add(new Employee("Palle Peterson", "FTE", "5678"));

            this.Add(new Employee("Amy E. Alberts", "CSG", "99222"));

            this.Add(new Employee("Stefan Hesse", "Vendor", "-"));

        }

    }

 

 

<Page x:Class="ScreenReaderTest.Window1"

    xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"

    xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"

    xmlns:src="clr-namespace:ScreenReaderTest"

   >

  <StackPanel>

    <ListBox TextSearch.TextPath="Name" >

      <!--ItemsSource is usually bound to a resource, but this

      is a shorcut for the example-->

      <ListBox.ItemsSource>

        <src:EmployeeList/>

      </ListBox.ItemsSource>

    </ListBox>

  </StackPanel>

</Page>

 

The ListBox displays the string returned By Employee.ToString. Accessibility tools such as Microsoft Narrator also uses the value of ToString of the selected item to read it to the user.

 

But there might be cases where the ToString method isn’t useful for screen readers. For example, the XMLDataProvider class is an easy way to bind a control to data, but if you use this, you should realize that the default information provided to UI Automation is useless. The following example uses a XmlDataProvider to display the same information as the previous example.

 

<Page x:Class="ScreenReaderTest.Window1"

    xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"

    xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"

    xmlns:src="clr-namespace:ScreenReaderTest"

   >

  <Page.Resources>

    <XmlDataProvider x:Key="Employees" XPath="/Employees/*">

      <x:XData>

        <Employees xmlns="">

          <Employee Name="Terry Adams"

                    Type="FTE" EmployeeNumber="1" />

          <Employee Name="Claire O&apos;Donnell"

                    Type="FTE" EmployeeNumber="12345" />

          <Employee Name="Palle Peterson"

                    Type="FTE" EmployeeNumber="5678" />

          <Employee Name="Amy E. Alberts"

                    Type="CSG" EmployeeNumber="99222" />

          <Employee Name="Stefan Hesse"

                    Type="Vendor" EmployeeNumber="-" />

        </Employees>

      </x:XData>

    </XmlDataProvider>

    <DataTemplate x:Key="EmployeeItemTemplate">

      <TextBlock Text="{Binding XPath=@Name}" />

    </DataTemplate>

  </Page.Resources>

  <StackPanel>

    <ListBox TextSearch.TextPath="@Name"

             ItemTemplate="{StaticResource EmployeeItemTemplate}"

             ItemsSource="{Binding Source={StaticResource Employees}}" >

    </ListBox>

  </StackPanel>

</Page>

 

In this case, the DataTemplate called EmployeeItemTemplate determines what is displayed in the ListBox, but Narrator still uses the ToString method to get the value of each item in the ListBox, which is simply the string, “System.Xml.XmlElement” To give something meaningful to Narrator, you can bind the AutomationProperties.Name property to the Employee.Name property.

 

    <ListBox ItemTemplate="{StaticResource EmployeeItemTemplate}"

             ItemsSource="{Binding Source={StaticResource Employees}}" >

      <ListBox.ItemContainerStyle>

        <Style>

          <Setter Property="AutomationProperties.Name"

                  Value="{Binding XPath=@Name}"/>

        </Style>

      </ListBox.ItemContainerStyle>

    </ListBox>

 

Now Narrator will read exactly the same thing that is displayed in the ListBox. The moral of the story is, if your data source doesn’t return an informative string in its ToString method, use the AutomationProperties.Name property to give something interesting to UI Automation.