diff --git a/ModernWpf.Controls/NumberBox/NumberBox.cs b/ModernWpf.Controls/NumberBox/NumberBox.cs index b1ff2cc6..469de58e 100644 --- a/ModernWpf.Controls/NumberBox/NumberBox.cs +++ b/ModernWpf.Controls/NumberBox/NumberBox.cs @@ -51,6 +51,8 @@ public partial class NumberBox : Control static NumberBox() { DefaultStyleKeyProperty.OverrideMetadata(typeof(NumberBox), new FrameworkPropertyMetadata(typeof(NumberBox))); + + AutomationProperties.NameProperty.OverrideMetadata(typeof(NumberBox), new FrameworkPropertyMetadata(OnAutomationPropertiesNamePropertyChanged)); } public NumberBox() @@ -331,6 +333,32 @@ private void OnIsEnabledChanged(object sender, DependencyPropertyChangedEventArg UpdateVisualStateForIsEnabledChange(); } + private static void OnAutomationPropertiesNamePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + ((NumberBox)d).ReevaluateForwardedUIAName(); + } + + private void ReevaluateForwardedUIAName() + { + if (m_textBox is { } textBox) + { + var name = AutomationProperties.GetName(this); + if (!string.IsNullOrEmpty(name)) + { + // AutomationProperties.Name is a non empty string, we will use that value. + AutomationProperties.SetName(textBox, name); + } + else + { + if (Header is string headerAsString) + { + // Header is a string, we can use that as our UIA name. + AutomationProperties.SetName(textBox, headerAsString); + } + } + } + } + private void UpdateVisualStateForIsEnabledChange() { VisualStateManager.GoToState(this, IsEnabled ? "Normal" : "Disabled", false); @@ -657,6 +685,11 @@ private void UpdateHeaderPresenterState() { // Header is not a string, so let's show header presenter shouldShowHeader = true; + // When our header isn't a string, we use the NumberBox's UIA name for the textbox's UIA name. + if (m_textBox is { } textBox) + { + AutomationProperties.SetName(textBox, AutomationProperties.GetName(this)); + } } } if (HeaderTemplate is { } headerTemplate) @@ -677,6 +710,8 @@ private void UpdateHeaderPresenterState() { m_headerPresenter.Visibility = shouldShowHeader ? Visibility.Visible : Visibility.Collapsed; } + + ReevaluateForwardedUIAName(); } private void MoveCaretToTextEnd() diff --git a/test/ModernWpfTestApp/ApiTests/NumberBoxTests.cs b/test/ModernWpfTestApp/ApiTests/NumberBoxTests.cs index 3813434a..2be0b8ea 100644 --- a/test/ModernWpfTestApp/ApiTests/NumberBoxTests.cs +++ b/test/ModernWpfTestApp/ApiTests/NumberBoxTests.cs @@ -12,6 +12,8 @@ using System.Windows.Controls.Primitives; using System.Windows; using System.Windows.Input; +using System.Windows.Automation.Peers; +using System.Windows.Automation; #if USING_TAEF using WEX.TestExecution; @@ -174,6 +176,53 @@ public void VerifyIsEnabledChangeUpdatesVisualState() }); } + [TestMethod] + public void VerifyUIANameBehavior() + { + NumberBox numberBox = null; + TextBox textBox = null; + + RunOnUIThread.Execute(() => + { + numberBox = new NumberBox(); + Content = numberBox; + Content.UpdateLayout(); + + textBox = TestPage.FindVisualChildrenByType(numberBox)[0]; + Verify.IsNotNull(textBox); + numberBox.Header = "Some header"; + }); + + IdleSynchronizer.Wait(); + + RunOnUIThread.Execute(() => + { + VerifyUIAName("Some header"); + numberBox.Header = new Button(); + AutomationProperties.SetName(numberBox, "Some UIA name"); + }); + + IdleSynchronizer.Wait(); + + RunOnUIThread.Execute(() => + { + VerifyUIAName("Some UIA name"); + numberBox.Header = new Button(); + }); + + IdleSynchronizer.Wait(); + + RunOnUIThread.Execute(() => + { + VerifyUIAName("Some UIA name"); + }); + + void VerifyUIAName(string value) + { + Verify.AreEqual(value, FrameworkElementAutomationPeer.CreatePeerForElement(textBox).GetName()); + } + } + private NumberBox SetupNumberBox() { NumberBox numberBox = null;