Make WPF App Accessible to screen reader - c#

I have a WPF application and part of the requirements are that it is accessible, including keyboard navigation and screen readers.
I have had some success with a a Treeview in the application by setting the AutomationProperties.Name in the ItemContainerStyle of the Treeview, but I am having problems with a Window that contains a text area and some buttons.
ZoomText will correctly read out the Title of the Window, but do so twice, as well as the text in the buttons, but I cannot get it to read the contents of the TextBlock.
The Text block is defined in a window as below. There are no binding errors showing up in the Visual Studio output while debugging, and the NVDA screen reader can read the content correctly, although this is not good enough for me as the customer uses ZoomText.
<Window x:Class="UserControls.ModalDialog"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="160" d:DesignWidth="400" MinHeight="85" MinWidth="400" MaxWidth="400" SizeToContent="Height" Height="Auto"
WindowStartupLocation="CenterScreen" ResizeMode="NoResize" Title="{Binding TitleText }">
<DockPanel Width="Auto" Margin="20,20,0,10">
<StackPanel Orientation="Vertical">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=DialogText, Mode=TwoWay}" Cursor="Arrow" Focusable="True" TextWrapping="WrapWithOverflow"
Height="Auto" Width="325" TextOptions.TextFormattingMode="Display"
ToolTip="{Binding Path=Text, RelativeSource={RelativeSource Self}}"
AutomationProperties.Name="{Binding Path=Text, RelativeSource={RelativeSource Self}}"
AutomationProperties.AutomationId="{Binding Path=Text, RelativeSource={RelativeSource Self}}">
</TextBlock>
</StackPanel>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right" Margin="0,10,5,0">
<Button Content="{Binding Path=Option1ButtonText, Mode=TwoWay}" Padding="5,0,5,0" Margin="0,20,5,0" MinWidth="100" IsDefault="True" Command="{Binding Path=Option1ButtonCommand, Mode=TwoWay}" />
<Button Content="{Binding Path=Option2ButtonText, Mode=TwoWay}" Padding="5,0,5,0" Margin="2,20,10,0" MinWidth="75" Command="{Binding Path=Option2ButtonCommand, Mode=TwoWay}" Visibility="{Binding Option2ButtonVisibility, Mode=TwoWay}"/>
<Button Content="{Binding Path=CancelButtonText, Mode=TwoWay}" Padding="5,0,5,0" Margin="2,20,10,0" MinWidth="75" IsCancel="True" Visibility="{Binding CancelButtonVisibility, Mode=TwoWay}"/>
</StackPanel>
</StackPanel>
</DockPanel>
If anyone has had any success with WPF and Screen readers and has any insight, or can point me in the right direction it would be great.
Update:
It seems the problem is because the TextBlock is within another element. If the window has the TextBlock as it's only element the screen reader reads the text correctly. However I need the Dock and Stack Panels for layout, so I need to find a way to get the Screen reader to work when the TextBlock is not the only content in the window.

You can do pretty much everything you need using the Automation Properties
For example;
Name
HelpText
Labels / LabeledBy
See documentation for more details on usage. Kind of surprised this wasn't answered by now considering the amount of upvotes. Either way, hope this helps. Cheers!

Related

why WPF context menu of a user control only show up particular places within the user control?

I m working on a noteBook project and i noticed one annoying behavior of context menu.
I have a user control in which i defined three textblocks. here is the code:
<UserControl x:Class="NoteBookPractice.View.Controls.NoteControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:NoteBookPractice.View"
mc:Ignorable="d"
d:DesignHeight="75" d:DesignWidth="400">
<StackPanel>
<TextBlock Text="{Binding Title}"
Margin="2"
VerticalAlignment="Center"
FontSize="13"
FontWeight="Bold"
Foreground="White"/>
<TextBlock Text="{Binding CreatedAt}"
Margin="2"
VerticalAlignment="Center"
FontSize="10"
FontWeight="Light"
Foreground="White"/>
<TextBlock Text="{Binding UpdatedAt}"
Margin="2"
VerticalAlignment="Center"
FontSize="10"
FontWeight="Light"
Foreground="White"/>
</StackPanel>
and here is the code in my window to use this user control:
<ListView DockPanel.Dock="Left"
Background="DarkCyan"
Width="150"
ItemsSource="{Binding Notes}"
SelectedValue="{Binding SelectedNote}">
<ListView.ItemTemplate>
<DataTemplate>
<uc:NoteControl Note="{Binding}">
<uc:NoteControl.ContextMenu>
<ContextMenu>
<MenuItem Header="Rename"
Command="{Binding Source={StaticResource vm}, Path=RenameNoteCommand}"
CommandParameter="{Binding}"/>
<MenuItem Header="Delete"
Command="{Binding Source={StaticResource vm}, Path=DeleteNoteCommand}"
CommandParameter="{Binding}"/>
</ContextMenu>
</uc:NoteControl.ContextMenu>
</uc:NoteControl>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
everything works great. But i noticed the context menu will note show when the user clicks places other than the text inside the user control.. see image below:
This is annoying because i thought the binding was not working. But then i clicked another place and it showed up. I guess a user will also be confused
is there any way i can make the context menu to show up wherever the user clicked on the note user control?
After a lot of search.. i found a way to do this. Adding this code to the listview body
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
</Style>
</ListView.ItemContainerStyle>
applying horizontalContentAlignment to user control directly or any of its parents seemed to be not working.

Element inside Flyout in XAML isn't accessible from Code Behind

I'm working on a UWP application and need some help with Flyout. I have a Flyout in my XAML with a few TextBlock elements but don't seem to be able to call those elements in the Code Behind. Everytime I try that, I get an Exception "The name TB does not exist in the current context."
I already searched for possible solutions and tried the following things: Made a clean build, Restarted VS 2017, Cleared the bin folder manually and then tried to rebuild
But nothing seems to work and I'm on a point where I don't know what to do.
<Page
x:Class="FuhrparkUWP.Pages.Parkhaeuser"
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:converter="using:FuhrparkUWP.Converter"
xmlns:data="using:FuhrparkStructureUWP.Model"
mc:Ignorable="d"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Page.Resources>
<converter:PassendFuerToStringConverter x:Key="PassendFuerToStringConverterKey"></converter:PassendFuerToStringConverter>
<converter:BelegtStatusToImageConverter x:Key="BelegtStatusToImageConverterKey"></converter:BelegtStatusToImageConverter>
</Page.Resources>
<Grid>
<ComboBox Name="CmbSelectParkhaus" Header="Parkhaus" HorizontalAlignment="Center" VerticalAlignment="Top" Width="200" SelectionChanged="CmbSelectParkhaus_SelectionChanged"/>
<GridView ItemsSource="{x:Bind Parkplaetze}" Name="ContentGrid" HorizontalAlignment="Center" VerticalAlignment="Top" Margin="0, 100, 0, 0">
<GridView.ItemTemplate>
<DataTemplate x:Name="ImageTextDataTemplate" x:DataType="data:Parkplatz">
<StackPanel Height="280" Width="180" Margin="12" Tapped="Content_Tapped">
<Image Source="{Binding Path=IstBelegt, Converter={StaticResource BelegtStatusToImageConverterKey}}" Height="180" Width="180" Stretch="UniformToFill"/>
<StackPanel Margin="0,12">
<TextBlock Text="{x:Bind FahrzeugKennzeichen}"/>
<TextBlock Text="{Binding Path=PassendFuer, Converter={StaticResource PassendFuerToStringConverterKey}}" Style="{ThemeResource CaptionTextBlockStyle}" Foreground="{ThemeResource SystemControlPageTextBaseMediumBrush}"/>
</StackPanel>
<FlyoutBase.AttachedFlyout>
<Flyout>
<StackPanel>
<Image Source="/Assets/Images/ParkplatzFrei.png" Width="180" Height="180"></Image>
<TextBlock Name="TB"></TextBlock>
<TextBlock Text="Passend für: LKW, PKW, Motorrad"></TextBlock>
<TextBlock Text="Belegt durch: FREI"></TextBlock>
</StackPanel>
</Flyout>
</FlyoutBase.AttachedFlyout>
</StackPanel>
</DataTemplate>
</GridView.ItemTemplate>
</GridView>
</Grid>
</Page>
I expect to call TB.Text = "xyz" for example from the Code Behind page, but at the moment I'm not able to call it.
I'm still able to call the other elements outside from the Flyout like "CmbSelectParkhaus" though.
You cannot handle things inside a DataTamplate, datamplates are not actual ui entities, they are Templates.
Assuming you want to achieve the same kind of access across all of your GridView items, you have to attach dependency properties, and either create programmatic binds on the PrepareContainerForItemOverride or the appropriate xaml "{Binding}" expressions.
This just opens whole new rabbit hole, especially if you are unaware of at least a single thing i mentioned so far, but you can just look up the bolded words one by one.

HeaderedContentControl header template not being used

I'm fairly new to WPF and am working with some legacy code, not sure how to use the HeaderedContentControl Header. I'd like to put in a StackPanel and customize the look of a header, just not sure how to do that.
Could someone give me some guidance on what to do next?
I have this xaml and the HeaderTemplate is never used.
<UserControl x:Class="PEC.Admin.WindowsControls.Program.Views.ProgramProductEnrichmentColorsView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:commonControls="clr-namespace:ManagerConsole.Common.Controls;assembly=ManagerConsole.Common.Controls"
xmlns:program="clr-namespace:PEC.Admin.ViewModel.Program;assembly=PEC.Admin.ViewModel.Program"
mc:Ignorable="d"
d:DesignWidth="300"
d:DataContext="{d:DesignInstance program:ProgramProductEnrichmentColorsViewModel}">
<commonControls:ExpanderPanel IsExpanded="{Binding Path=IsExpanded,Mode=TwoWay}">
<HeaderedContentControl.HeaderTemplate> <!-- this never gets used... -->
<DataTemplate>
<StackPanel>
<Label Content="{Binding Path=Header}"></Label>
</StackPanel>
</DataTemplate>
</HeaderedContentControl.HeaderTemplate>
<StackPanel HorizontalAlignment="Stretch"
VerticalAlignment="Top"
Width="Auto"
Margin="3"
Background="White">
<TextBlock Text="Source Type:"
Margin="0,5,0,0" />
<TextBox IsReadOnly="True"
IsTabStop="False"
Background="LightGray"
BorderThickness="0"
Text="{Binding Path=SourceTypeName, Mode=OneTime}" />
</StackPanel>
</commonControls:ExpanderPanel>
</UserControl>
HeaderTemplate is applied. To verify it - set a background to the Label in HeaderTemplate.
HeaderTemplate doesn't display anything though, because binding is incorrect. Template is applied to the data set in Header property, which currently has null value.
So change code as in the example below (I tried with Expander, hopefully it will work for custom commonControls:ExpanderPanel):
<Expander IsExpanded="{Binding Path=IsExpanded, Mode=TwoWay}"
Header="{Binding ComplexObject}">
<HeaderedContentControl.HeaderTemplate>
<DataTemplate>
<StackPanel>
<Label Background="Green" Content="{Binding PropertyOfTheObject}"/>
</StackPanel>
</DataTemplate>
</HeaderedContentControl.HeaderTemplate>
</Expander>
Header is a dependency property and can be set via Binding. Header becomes a source for bindings in a HeaderTemplate. Or it can be some constant (Header="Click to expand"), resource (Header="{StaticResource ExpandTitle}") or complex content, e.g.:
<Expander.Header>
<TextBlock Text="Click to expand"/>
</Expander.Header>

Responsive Windows App Element

Is there any XAML element that will automatically wrap to a new line for sub elements that don't have enough room on a line?
What I mean is that on a wide screen I'll get:
Box1 Box2 Box3
And on a narrow one:
Box1 Box2
Box3
Without having to listen to events and fix it by code.
The comments mention VariableSizedWrapGrid as a solution. But I can't figure out how to make it wrap.
You can use a simple GridView to do it. If you don't set a Height, it should work like a charm.
<GridView Name="xConcerts" ItemsSource="{x:Bind Artist.UpcomingEvents, Mode=OneWay}">
<GridView.ItemTemplate>
<DataTemplate x:DataType="songkick:EventExt">
<Border CornerRadius="8" Background="{ThemeResource ThemeGrayHighColorBrush}" Opacity="0.8">
<StackPanel Margin="18,2">
<TextBlock Text="{x:Bind FullDisplayDate, Converter={StaticResource FormatStringToDateDayConverter}}" Style="{ThemeResource ThemeDateBoldStyle}"/>
<TextBlock Text="{x:Bind FullDisplayDate, Converter={StaticResource FormatStringToDateMonthConverter}}" Style="{ThemeResource ThemeDateBoldStyle}" Margin="0,-4,0,0"/>
</StackPanel>
</Border>
</DataTemplate>
</GridView.ItemTemplate>
<GridView.ItemContainerTransitions>
<TransitionCollection>
<RepositionThemeTransition/>
</TransitionCollection>
</GridView.ItemContainerTransitions>
</GridView>
According to your requirement, using the WrapPanel control in WinRTXamlToolkit will meet your needs.
Sample code here:
<Page x:Class="TestDemo.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:Tool="using:WinRTXamlToolkit.Controls"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="using:TestDemo"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Tool:WrapPanel>
<Button Width="200" Margin="10">1</Button>
<Button Width="200" Margin="10">2</Button>
<Button Width="200" Margin="10">3</Button>
</Tool:WrapPanel>
</Page>
And the output here:

TreeView doesn't have the AfterExpand event?

I try to have a treeview that browse potentially cyclic hierarchical data.
This means that I cannot try to load all the tree at once, since there maybe be infinite loops then.
I would like to react to the TreeView.AfterCollapse Event documented here at MSDN
however, my control doesnt seem to have this event. If I try to add the AfterExpand attribute, i get this error message:
error MC3072: The property 'AfterExpand' does not exist in XML namespace 'http://schemas.microsoft.com/winfx/2006/xaml/presentation'. Line 23 Position 21.
What I am doing wrong ? Calling a wrong namespace ?
Here is the code:
<Window x:Class="MyApp.Edit.EditView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:MyApp.Edit"
Title="{Binding WindowTitle,UpdateSourceTrigger=PropertyChanged}" MinHeight="350" MinWidth="350">
<Window.Resources>
<HierarchicalDataTemplate x:Key="sectionTemplate"
ItemsSource="{Binding ChildSections}"
DataType="{x:Type local:Section}">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Label, UpdateSourceTrigger=PropertyChanged}" />
<TextBlock Text=" - " />
<TextBlock Text="{Binding Description}" FontStyle="Italic" Foreground="#777" />
</StackPanel>
</HierarchicalDataTemplate>
</Window.Resources>
<StackPanel>
<TreeView ItemsSource="{Binding Sections}"
SelectedItemChanged="TreeView_SelectedItemChanged"
ItemTemplate="{StaticResource sectionTemplate}"
MinHeight="150"
MinWidth="300"
Name="treeView"
AfterExpand="MyEventHandler"
</TreeView>
<TextBox Text="{Binding Label, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Margin="0 10 0 0"/>
<TextBox Text="{Binding Description, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Margin="0 10 0 0"/>
<StackPanel Orientation="Horizontal">
<Button Content="Add Child" Click="Button_Click_AddChild" />
</StackPanel>
</StackPanel>
</Window>
That is a Windows Forms tree view event, it does not belong to the WPF TreeView, in WPF you can use Collapsed and Expanded of the TreeViewItems, not the TreeView itself.
However you can subscribe to the events on the TreeView as they are routed.

Categories

Resources