diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Input/TokenizingTextBox/TokenizingTextBox.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Input/TokenizingTextBox/TokenizingTextBox.cs
index 89474a89fc6..0c22e8336fa 100644
--- a/Microsoft.Toolkit.Uwp.UI.Controls.Input/TokenizingTextBox/TokenizingTextBox.cs
+++ b/Microsoft.Toolkit.Uwp.UI.Controls.Input/TokenizingTextBox/TokenizingTextBox.cs
@@ -2,15 +2,16 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
-using System;
using System.Collections.ObjectModel;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Toolkit.Uwp.Deferred;
+using Microsoft.Toolkit.Uwp.UI.Automation.Peers;
using Microsoft.Toolkit.Uwp.UI.Helpers;
using Windows.System;
using Windows.UI.Core;
using Windows.UI.Xaml;
+using Windows.UI.Xaml.Automation.Peers;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Input;
@@ -486,6 +487,15 @@ protected void UpdateCurrentTextEdit(ITokenStringContainer edit)
Text = edit.Text; // Update our text property.
}
+ ///
+ /// Creates AutomationPeer ()
+ ///
+ /// An automation peer for this .
+ protected override AutomationPeer OnCreateAutomationPeer()
+ {
+ return new TokenizingTextBoxAutomationPeer(this);
+ }
+
///
/// Remove the specified token from the list.
///
diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Input/TokenizingTextBox/TokenizingTextBoxAutomationPeer.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Input/TokenizingTextBox/TokenizingTextBoxAutomationPeer.cs
new file mode 100644
index 00000000000..5f049a80149
--- /dev/null
+++ b/Microsoft.Toolkit.Uwp.UI.Controls.Input/TokenizingTextBox/TokenizingTextBoxAutomationPeer.cs
@@ -0,0 +1,131 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Collections.Generic;
+using Microsoft.Toolkit.Uwp.UI.Controls;
+using Windows.UI.Xaml.Automation;
+using Windows.UI.Xaml.Automation.Peers;
+using Windows.UI.Xaml.Automation.Provider;
+using Windows.UI.Xaml.Controls;
+
+namespace Microsoft.Toolkit.Uwp.UI.Automation.Peers
+{
+ ///
+ /// Defines a framework element automation peer for the control.
+ ///
+ public class TokenizingTextBoxAutomationPeer : ListViewBaseAutomationPeer, IValueProvider
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ ///
+ /// The that is associated with this .
+ ///
+ public TokenizingTextBoxAutomationPeer(TokenizingTextBox owner)
+ : base(owner)
+ {
+ }
+
+ /// Gets a value indicating whether the value of a control is read-only.
+ /// **true** if the value is read-only; **false** if it can be modified.
+ public bool IsReadOnly => !this.OwningTokenizingTextBox.IsEnabled;
+
+ /// Gets the value of the control.
+ /// The value of the control.
+ public string Value => this.OwningTokenizingTextBox.Text;
+
+ private TokenizingTextBox OwningTokenizingTextBox
+ {
+ get
+ {
+ return Owner as TokenizingTextBox;
+ }
+ }
+
+ /// Sets the value of a control.
+ /// The value to set. The provider is responsible for converting the value to the appropriate data type.
+ /// Thrown if the control is in a read-only state.
+ public void SetValue(string value)
+ {
+ if (IsReadOnly)
+ {
+ throw new ElementNotEnabledException($"Could not set the value of the {nameof(TokenizingTextBox)} ");
+ }
+
+ this.OwningTokenizingTextBox.Text = value;
+ }
+
+ ///
+ /// Called by GetClassName that gets a human readable name that, in addition to AutomationControlType,
+ /// differentiates the control represented by this AutomationPeer.
+ ///
+ /// The string that contains the name.
+ protected override string GetClassNameCore()
+ {
+ return Owner.GetType().Name;
+ }
+
+ ///
+ /// Called by GetName.
+ ///
+ ///
+ /// Returns the first of these that is not null or empty:
+ /// - Value returned by the base implementation
+ /// - Name of the owning TokenizingTextBox
+ /// - TokenizingTextBox class name
+ ///
+ protected override string GetNameCore()
+ {
+ string name = this.OwningTokenizingTextBox.Name;
+ if (!string.IsNullOrWhiteSpace(name))
+ {
+ return name;
+ }
+
+ name = AutomationProperties.GetName(this.OwningTokenizingTextBox);
+ return !string.IsNullOrWhiteSpace(name) ? name : base.GetNameCore();
+ }
+
+ ///
+ /// Gets the control pattern that is associated with the specified Windows.UI.Xaml.Automation.Peers.PatternInterface.
+ ///
+ /// A value from the Windows.UI.Xaml.Automation.Peers.PatternInterface enumeration.
+ /// The object that supports the specified pattern, or null if unsupported.
+ protected override object GetPatternCore(PatternInterface patternInterface)
+ {
+ return patternInterface switch
+ {
+ PatternInterface.Value => this,
+ _ => base.GetPatternCore(patternInterface)
+ };
+ }
+
+ ///
+ /// Gets the collection of elements that are represented in the UI Automation tree as immediate
+ /// child elements of the automation peer.
+ ///
+ /// The children elements.
+ protected override IList GetChildrenCore()
+ {
+ TokenizingTextBox owner = this.OwningTokenizingTextBox;
+
+ ItemCollection items = owner.Items;
+ if (items.Count <= 0)
+ {
+ return null;
+ }
+
+ List peers = new List(items.Count);
+ for (int i = 0; i < items.Count; i++)
+ {
+ if (owner.ContainerFromIndex(i) is TokenizingTextBoxItem element)
+ {
+ peers.Add(FromElement(element) ?? CreatePeerForElement(element));
+ }
+ }
+
+ return peers;
+ }
+ }
+}
diff --git a/UnitTests/UnitTests.UWP/UI/Controls/Test_TokenizingTextBox_AutomationPeer.cs b/UnitTests/UnitTests.UWP/UI/Controls/Test_TokenizingTextBox_AutomationPeer.cs
new file mode 100644
index 00000000000..bf6e2ed358c
--- /dev/null
+++ b/UnitTests/UnitTests.UWP/UI/Controls/Test_TokenizingTextBox_AutomationPeer.cs
@@ -0,0 +1,125 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Linq;
+using System.Threading.Tasks;
+using Windows.UI.Xaml.Automation;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using Windows.UI.Xaml.Automation.Peers;
+using Microsoft.Toolkit.Uwp;
+using Microsoft.Toolkit.Uwp.UI.Automation.Peers;
+using Microsoft.Toolkit.Uwp.UI.Controls;
+
+namespace UnitTests.UWP.UI.Controls
+{
+ [TestClass]
+ [TestCategory("Test_TokenizingTextBox")]
+ public class Test_TokenizingTextBox_AutomationPeer : VisualUITestBase
+ {
+ [TestMethod]
+ public async Task ShouldConfigureTokenizingTextBoxAutomationPeerAsync()
+ {
+ await App.DispatcherQueue.EnqueueAsync(async () =>
+ {
+ const string expectedAutomationName = "MyAutomationName";
+ const string expectedName = "MyName";
+ const string expectedValue = "Wor";
+
+ var items = new ObservableCollection { new() { Title = "Hello" }, new() { Title = "World" } };
+
+ var tokenizingTextBox = new TokenizingTextBox { ItemsSource = items };
+
+ await SetTestContentAsync(tokenizingTextBox);
+
+ var tokenizingTextBoxAutomationPeer =
+ FrameworkElementAutomationPeer.CreatePeerForElement(tokenizingTextBox) as TokenizingTextBoxAutomationPeer;
+
+ Assert.IsNotNull(tokenizingTextBoxAutomationPeer, "Verify that the AutomationPeer is TokenizingTextBoxAutomationPeer.");
+
+ // Asserts the automation peer name based on the Automation Property Name value.
+ tokenizingTextBox.SetValue(AutomationProperties.NameProperty, expectedAutomationName);
+ Assert.IsTrue(tokenizingTextBoxAutomationPeer.GetName().Contains(expectedAutomationName), "Verify that the UIA name contains the given AutomationProperties.Name of the TokenizingTextBox.");
+
+ // Asserts the automation peer name based on the element Name property.
+ tokenizingTextBox.Name = expectedName;
+ Assert.IsTrue(tokenizingTextBoxAutomationPeer.GetName().Contains(expectedName), "Verify that the UIA name contains the given Name of the TokenizingTextBox.");
+
+ tokenizingTextBoxAutomationPeer.SetValue(expectedValue);
+ Assert.IsTrue(tokenizingTextBoxAutomationPeer.Value.Equals(expectedValue), "Verify that the Value contains the given Text of the TokenizingTextBox.");
+ });
+ }
+
+ [TestMethod]
+ public async Task ShouldReturnTokensForTokenizingTextBoxAutomationPeerAsync()
+ {
+ await App.DispatcherQueue.EnqueueAsync(async () =>
+ {
+ var items = new ObservableCollection
+ {
+ new() { Title = "Hello" }, new() { Title = "World" }
+ };
+
+ var tokenizingTextBox = new TokenizingTextBox { ItemsSource = items };
+
+ await SetTestContentAsync(tokenizingTextBox);
+
+ tokenizingTextBox
+ .SelectAllTokensAndText(); // Will be 3 items due to the `AndText` that will select an empty text item.
+
+ var tokenizingTextBoxAutomationPeer =
+ FrameworkElementAutomationPeer.CreatePeerForElement(tokenizingTextBox) as
+ TokenizingTextBoxAutomationPeer;
+
+ Assert.IsNotNull(
+ tokenizingTextBoxAutomationPeer,
+ "Verify that the AutomationPeer is TokenizingTextBoxAutomationPeer.");
+
+ var selectedItems = tokenizingTextBoxAutomationPeer
+ .GetChildren()
+ .Cast()
+ .Select(peer => peer.Owner as TokenizingTextBoxItem)
+ .Select(item => item?.Content as TokenizingTextBoxTestItem)
+ .ToList();
+
+ Assert.AreEqual(3, selectedItems.Count);
+ Assert.AreEqual(items[0], selectedItems[0]);
+ Assert.AreEqual(items[1], selectedItems[1]);
+ Assert.IsNull(selectedItems[2]); // The 3rd item is the empty text item.
+ });
+ }
+
+ [TestMethod]
+ public async Task ShouldThrowElementNotEnabledExceptionIfValueSetWhenDisabled()
+ {
+ await App.DispatcherQueue.EnqueueAsync(async () =>
+ {
+ const string expectedValue = "Wor";
+
+ var tokenizingTextBox = new TokenizingTextBox { IsEnabled = false };
+
+ await SetTestContentAsync(tokenizingTextBox);
+
+ var tokenizingTextBoxAutomationPeer =
+ FrameworkElementAutomationPeer.CreatePeerForElement(tokenizingTextBox) as TokenizingTextBoxAutomationPeer;
+
+ Assert.ThrowsException(() =>
+ {
+ tokenizingTextBoxAutomationPeer.SetValue(expectedValue);
+ });
+ });
+ }
+
+ public class TokenizingTextBoxTestItem
+ {
+ public string Title { get; set; }
+
+ public override string ToString()
+ {
+ return Title;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/UnitTests/UnitTests.UWP/UnitTests.UWP.csproj b/UnitTests/UnitTests.UWP/UnitTests.UWP.csproj
index 0323d553a9a..ae9050b2bc8 100644
--- a/UnitTests/UnitTests.UWP/UnitTests.UWP.csproj
+++ b/UnitTests/UnitTests.UWP/UnitTests.UWP.csproj
@@ -223,6 +223,7 @@
+