fix: fix accessibility for dynamic generated form.
This commit is contained in:
@@ -59,6 +59,7 @@
|
|||||||
HorizontalAlignment="{TemplateBinding LabelAlignment}"
|
HorizontalAlignment="{TemplateBinding LabelAlignment}"
|
||||||
Orientation="Horizontal">
|
Orientation="Horizontal">
|
||||||
<Label
|
<Label
|
||||||
|
Name="PART_Label"
|
||||||
Content="{TemplateBinding Label}"
|
Content="{TemplateBinding Label}"
|
||||||
Background="Transparent"
|
Background="Transparent"
|
||||||
FontWeight="{DynamicResource TextBlockTitleFontWeight}"
|
FontWeight="{DynamicResource TextBlockTitleFontWeight}"
|
||||||
@@ -98,8 +99,7 @@
|
|||||||
Name="PART_Label"
|
Name="PART_Label"
|
||||||
Content="{TemplateBinding Label}"
|
Content="{TemplateBinding Label}"
|
||||||
Background="Transparent"
|
Background="Transparent"
|
||||||
FontWeight="{DynamicResource TextBlockTitleFontWeight}"
|
FontWeight="{DynamicResource TextBlockTitleFontWeight}"/>
|
||||||
Target="{Binding #PART_ContentPresenter.Content}" />
|
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Foreground="{DynamicResource FormAsteriskForeground}"
|
Foreground="{DynamicResource FormAsteriskForeground}"
|
||||||
IsVisible="{TemplateBinding IsRequired}"
|
IsVisible="{TemplateBinding IsRequired}"
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
using Avalonia;
|
using Avalonia;
|
||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
using Avalonia.Controls.Metadata;
|
using Avalonia.Controls.Metadata;
|
||||||
|
using Avalonia.Controls.Primitives;
|
||||||
|
using Avalonia.Input;
|
||||||
|
using Avalonia.Interactivity;
|
||||||
using Avalonia.Layout;
|
using Avalonia.Layout;
|
||||||
using Avalonia.Reactive;
|
using Avalonia.Reactive;
|
||||||
using Avalonia.VisualTree;
|
using Avalonia.VisualTree;
|
||||||
@@ -10,8 +13,10 @@ using Ursa.Common;
|
|||||||
namespace Ursa.Controls;
|
namespace Ursa.Controls;
|
||||||
|
|
||||||
[PseudoClasses(PC_Horizontal, PC_NoLabel)]
|
[PseudoClasses(PC_Horizontal, PC_NoLabel)]
|
||||||
|
[TemplatePart(PART_Label, typeof(Label))]
|
||||||
public class FormItem: ContentControl
|
public class FormItem: ContentControl
|
||||||
{
|
{
|
||||||
|
public const string PART_Label = "PART_Label";
|
||||||
public const string PC_Horizontal = ":horizontal";
|
public const string PC_Horizontal = ":horizontal";
|
||||||
public const string PC_NoLabel = ":no-label";
|
public const string PC_NoLabel = ":no-label";
|
||||||
|
|
||||||
@@ -33,7 +38,8 @@ public class FormItem: ContentControl
|
|||||||
public static void SetNoLabel(Control obj, bool value) => obj.SetValue(NoLabelProperty, value);
|
public static void SetNoLabel(Control obj, bool value) => obj.SetValue(NoLabelProperty, value);
|
||||||
public static bool GetNoLabel(Control obj) => obj.GetValue(NoLabelProperty);
|
public static bool GetNoLabel(Control obj) => obj.GetValue(NoLabelProperty);
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
private Label? _label;
|
||||||
private List<IDisposable> _formSubscriptions = new List<IDisposable>();
|
private List<IDisposable> _formSubscriptions = new List<IDisposable>();
|
||||||
|
|
||||||
public static readonly StyledProperty<double> LabelWidthProperty = AvaloniaProperty.Register<FormItem, double>(
|
public static readonly StyledProperty<double> LabelWidthProperty = AvaloniaProperty.Register<FormItem, double>(
|
||||||
@@ -57,6 +63,8 @@ public class FormItem: ContentControl
|
|||||||
static FormItem()
|
static FormItem()
|
||||||
{
|
{
|
||||||
NoLabelProperty.AffectsPseudoClass<FormItem>(PC_NoLabel);
|
NoLabelProperty.AffectsPseudoClass<FormItem>(PC_NoLabel);
|
||||||
|
LabelProperty.Changed.AddClassHandler<FormItem>((o, e) => o.SetLabelTarget());
|
||||||
|
ContentProperty.Changed.AddClassHandler<FormItem>((o, e) => o.SetLabelTarget());
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
|
protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
|
||||||
@@ -81,6 +89,36 @@ public class FormItem: ContentControl
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
|
||||||
|
{
|
||||||
|
base.OnApplyTemplate(e);
|
||||||
|
_label = e.NameScope.Find<Label>(PART_Label);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnLoaded(RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
base.OnLoaded(e);
|
||||||
|
SetLabelTarget();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SetLabelTarget()
|
||||||
|
{
|
||||||
|
if (_label is null) return;
|
||||||
|
// Set it directly if content is a control, this is faster than looking up logical tree.
|
||||||
|
if (Content is InputElement input)
|
||||||
|
{
|
||||||
|
_label.Target = input;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var logical = this.LogicalChildren.OfType<InputElement>().FirstOrDefault(a => a.Focusable);
|
||||||
|
if (logical is not null)
|
||||||
|
{
|
||||||
|
_label.Target = logical;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e)
|
protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e)
|
||||||
{
|
{
|
||||||
base.OnDetachedFromVisualTree(e);
|
base.OnDetachedFromVisualTree(e);
|
||||||
|
|||||||
@@ -26,6 +26,24 @@ public class FormAccessibilityTests
|
|||||||
Assert.False(form.NameBox.IsFocused);
|
Assert.False(form.NameBox.IsFocused);
|
||||||
Assert.True(form.EmailBox.IsFocused);
|
Assert.True(form.EmailBox.IsFocused);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[AvaloniaFact]
|
||||||
|
public void Static_Form_With_FormItem_Accessible()
|
||||||
|
{
|
||||||
|
var window = new Window();
|
||||||
|
var form = new StaticForm();
|
||||||
|
window.Content = form;
|
||||||
|
window.Show();
|
||||||
|
|
||||||
|
Assert.False(form.NameBox.IsFocused);
|
||||||
|
Assert.False(form.EmailBox.IsFocused);
|
||||||
|
window.KeyPressQwerty(PhysicalKey.N, RawInputModifiers.Alt);
|
||||||
|
Assert.True(form.NameBox.IsFocused);
|
||||||
|
Assert.False(form.EmailBox.IsFocused);
|
||||||
|
window.KeyPressQwerty(PhysicalKey.E, RawInputModifiers.Alt);
|
||||||
|
Assert.False(form.NameBox.IsFocused);
|
||||||
|
Assert.True(form.EmailBox.IsFocused);
|
||||||
|
}
|
||||||
|
|
||||||
[AvaloniaFact]
|
[AvaloniaFact]
|
||||||
public void Dynamic_Form_Inner_Control_Accessible()
|
public void Dynamic_Form_Inner_Control_Accessible()
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
mc:Ignorable="d" d:DesignWidth="800"
|
mc:Ignorable="d" d:DesignWidth="800"
|
||||||
d:DesignHeight="450"
|
d:DesignHeight="450"
|
||||||
x:Class="HeadlessTest.Ursa.Controls.FormTests.AccessibilityTests.StaticForm">
|
x:Class="HeadlessTest.Ursa.Controls.FormTests.AccessibilityTests.StaticForm">
|
||||||
<u:Form>
|
<u:Form LabelWidth="200" LabelPosition="Left" LabelAlignment="Left">
|
||||||
<TextBox Name="NameBox" Width="300" u:FormItem.Label="_Name" />
|
<TextBox Name="NameBox" Width="300" u:FormItem.Label="_Name" />
|
||||||
<TextBox Name="EmailBox" Width="300" u:FormItem.Label="_Email" />
|
<TextBox Name="EmailBox" Width="300" u:FormItem.Label="_Email" />
|
||||||
</u:Form>
|
</u:Form>
|
||||||
|
|||||||
@@ -0,0 +1,17 @@
|
|||||||
|
<UserControl xmlns="https://github.com/avaloniaui"
|
||||||
|
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:u="https://irihi.tech/ursa"
|
||||||
|
mc:Ignorable="d" d:DesignWidth="800"
|
||||||
|
d:DesignHeight="450"
|
||||||
|
x:Class="HeadlessTest.Ursa.Controls.FormTests.AccessibilityTests.StaticForm2">
|
||||||
|
<u:Form LabelWidth="200" LabelPosition="Left" LabelAlignment="Left">
|
||||||
|
<u:FormItem Label="_Name">
|
||||||
|
<TextBox Name="NameBox" Width="300" />
|
||||||
|
</u:FormItem>
|
||||||
|
<u:FormItem Label="_Email">
|
||||||
|
<TextBox Name="EmailBox" Width="300" />
|
||||||
|
</u:FormItem>
|
||||||
|
</u:Form>
|
||||||
|
</UserControl>
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
using Avalonia;
|
||||||
|
using Avalonia.Controls;
|
||||||
|
using Avalonia.Markup.Xaml;
|
||||||
|
|
||||||
|
namespace HeadlessTest.Ursa.Controls.FormTests.AccessibilityTests;
|
||||||
|
|
||||||
|
public partial class StaticForm2 : UserControl
|
||||||
|
{
|
||||||
|
public StaticForm2()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user