diff --git a/dev/NumberBox/APITests/NumberBoxTests.cs b/dev/NumberBox/APITests/NumberBoxTests.cs index 25a930d818..95e6372b0d 100644 --- a/dev/NumberBox/APITests/NumberBoxTests.cs +++ b/dev/NumberBox/APITests/NumberBoxTests.cs @@ -11,6 +11,9 @@ using Microsoft.UI.Xaml.Controls; using Windows.UI.Xaml.Controls.Primitives; using Windows.UI.Xaml.Input; +using MUXControls.TestAppUtils; +using Windows.UI.Xaml.Automation.Peers; +using Windows.UI.Xaml.Automation; #if USING_TAEF using WEX.TestExecution; @@ -49,7 +52,7 @@ public void VerifyTextAlignmentPropogates() [TestMethod] public void VerifyNumberBoxCornerRadius() { - if (PlatformConfiguration.IsOSVersionLessThan(OSVersion.Redstone5)) + if (Common.PlatformConfiguration.IsOSVersionLessThan(Common.OSVersion.Redstone5)) { Log.Warning("NumberBox CornerRadius property is not available pre-rs5"); return; @@ -171,6 +174,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; diff --git a/dev/NumberBox/NumberBox.cpp b/dev/NumberBox/NumberBox.cpp index 82e055b573..06904a1454 100644 --- a/dev/NumberBox/NumberBox.cpp +++ b/dev/NumberBox/NumberBox.cpp @@ -46,6 +46,10 @@ NumberBox::NumberBox() SetDefaultStyleKey(this); SetDefaultInputScope(); + + // We are not revoking this since the event and the listener reside on the same object and as such have the same lifecycle. + // That means that as soon as the NumberBox gets removed so will the event and the listener. + this->RegisterPropertyChangedCallback(winrt::AutomationProperties::NameProperty(), { this , &NumberBox::OnAutomationPropertiesNamePropertyChanged }); } void NumberBox::SetDefaultInputScope() @@ -361,6 +365,32 @@ void NumberBox::OnIsEnabledChanged(const winrt::IInspectable&, const winrt::Depe UpdateVisualStateForIsEnabledChange(); } +void NumberBox::OnAutomationPropertiesNamePropertyChanged(const winrt::DependencyObject&, const winrt::DependencyProperty&) +{ + ReevaluateForwardedUIAName(); +} + +void NumberBox::ReevaluateForwardedUIAName() +{ + if (const auto textBox = m_textBox.get()) + { + const auto name = winrt::AutomationProperties::GetName(*this); + if (!name.empty()) + { + // AutomationProperties.Name is a non empty string, we will use that value. + winrt::AutomationProperties::SetName(textBox, name); + } + else + { + if (const auto headerAsString = Header().try_as>()) + { + // Header is a string, we can use that as our UIA name. + winrt::AutomationProperties::SetName(textBox, headerAsString.Value()); + } + } + } +} + void NumberBox::UpdateVisualStateForIsEnabledChange() { winrt::VisualStateManager::GoToState(*this, IsEnabled() ? L"Normal" : L"Disabled", false); @@ -686,6 +716,11 @@ void NumberBox::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 (const auto textBox = m_textBox.get()) + { + winrt::AutomationProperties::SetName(textBox, winrt::AutomationProperties::GetName(*this)); + } } } if(const auto headerTemplate = HeaderTemplate()) @@ -706,6 +741,8 @@ void NumberBox::UpdateHeaderPresenterState() { headerPresenter.Visibility(shouldShowHeader ? winrt::Visibility::Visible : winrt::Visibility::Collapsed); } + + ReevaluateForwardedUIAName(); } void NumberBox::MoveCaretToTextEnd() diff --git a/dev/NumberBox/NumberBox.h b/dev/NumberBox/NumberBox.h index 58e1e3ba23..c8c48b8473 100644 --- a/dev/NumberBox/NumberBox.h +++ b/dev/NumberBox/NumberBox.h @@ -70,6 +70,9 @@ class NumberBox : void OnNumberBoxScroll(winrt::IInspectable const& sender, winrt::PointerRoutedEventArgs const& args); void OnCornerRadiusPropertyChanged(const winrt::DependencyObject&, const winrt::DependencyProperty&); void OnIsEnabledChanged(const winrt::IInspectable&, const winrt::DependencyPropertyChangedEventArgs&); + void OnAutomationPropertiesNamePropertyChanged(const winrt::DependencyObject&, const winrt::DependencyProperty&); + + void ReevaluateForwardedUIAName(); void ValidateInput(); void CoerceMinimum();