diff --git a/Microsoft.Toolkit.Uwp.SampleApp/Data/PhotoDataItem.cs b/Microsoft.Toolkit.Uwp.SampleApp/Data/PhotoDataItem.cs
index ae16e5eaaf8..37a9eb0f5de 100644
--- a/Microsoft.Toolkit.Uwp.SampleApp/Data/PhotoDataItem.cs
+++ b/Microsoft.Toolkit.Uwp.SampleApp/Data/PhotoDataItem.cs
@@ -11,5 +11,10 @@ public class PhotoDataItem
public string Category { get; set; }
public string Thumbnail { get; set; }
+
+ public override string ToString()
+ {
+ return Title;
+ }
}
}
diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/BladeView/BladeItemAutomationPeer.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/BladeView/BladeItemAutomationPeer.cs
index a45a1acf191..f07b3e82581 100644
--- a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/BladeView/BladeItemAutomationPeer.cs
+++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/BladeView/BladeItemAutomationPeer.cs
@@ -93,28 +93,6 @@ protected override string GetNameCore()
return string.Empty;
}
- ///
- /// Called by GetAutomationId that gets the **AutomationId** of the element that is associated with the automation peer.
- ///
- ///
- /// The string that contains the automation ID.
- ///
- protected override string GetAutomationIdCore()
- {
- string automationId = base.GetAutomationIdCore();
- if (!string.IsNullOrEmpty(automationId))
- {
- return automationId;
- }
-
- if (this.OwnerBladeItem != null)
- {
- return this.GetNameCore();
- }
-
- return string.Empty;
- }
-
///
/// Returns the size of the set where the element that is associated with the automation peer is located.
///
diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/Carousel/Carousel.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/Carousel/Carousel.cs
index 1d73c58e181..ababf502fe3 100644
--- a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/Carousel/Carousel.cs
+++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/Carousel/Carousel.cs
@@ -5,9 +5,11 @@
using System;
using System.Collections.Generic;
using System.Linq;
+using Microsoft.Toolkit.Uwp.UI.Automation.Peers;
using Windows.Foundation;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Automation;
+using Windows.UI.Xaml.Automation.Peers;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
@@ -520,6 +522,17 @@ protected override void PrepareContainerForItemOverride(DependencyObject element
{
carouselItem.IsSelected = true;
}
+
+ carouselItem.ParentCarousel = this;
+ }
+
+ ///
+ /// Creates AutomationPeer ()
+ ///
+ /// An automation peer for this .
+ protected override AutomationPeer OnCreateAutomationPeer()
+ {
+ return new CarouselAutomationPeer(this);
}
private void OnCarouselItemSelected(object sender, EventArgs e)
@@ -528,5 +541,11 @@ private void OnCarouselItemSelected(object sender, EventArgs e)
SelectedItem = ItemFromContainer(item);
}
+
+ internal void SetSelectedItem(CarouselItem owner)
+ {
+ var item = ItemFromContainer(owner);
+ SelectedItem = item;
+ }
}
}
diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/Carousel/CarouselAutomationPeer.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/Carousel/CarouselAutomationPeer.cs
new file mode 100644
index 00000000000..59a310ec3b4
--- /dev/null
+++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/Carousel/CarouselAutomationPeer.cs
@@ -0,0 +1,143 @@
+// 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 CarouselAutomationPeer : ItemsControlAutomationPeer, ISelectionProvider
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ ///
+ /// The that is associated with this .
+ ///
+ public CarouselAutomationPeer(Carousel owner)
+ : base(owner)
+ {
+ }
+
+ /// Gets a value indicating whether the Microsoft UI Automation provider allows more than one child element to be selected concurrently.
+ /// True if multiple selection is allowed; otherwise, false.
+ public bool CanSelectMultiple => false;
+
+ /// Gets a value indicating whether the UI Automation provider requires at least one child element to be selected.
+ /// True if selection is required; otherwise, false.
+ public bool IsSelectionRequired => true;
+
+ private Carousel OwningCarousel
+ {
+ get
+ {
+ return Owner as Carousel;
+ }
+ }
+
+ /// Retrieves a UI Automation provider for each child element that is selected.
+ /// An array of UI Automation providers.
+ public IRawElementProviderSimple[] GetSelection()
+ {
+ return OwningCarousel.ContainerFromItem(this.OwningCarousel.SelectedItem) is CarouselItem selectedCarouselItem
+ ? new[] { this.ProviderFromPeer(FromElement(selectedCarouselItem)) }
+ : new IRawElementProviderSimple[] { };
+ }
+
+ ///
+ /// Gets the control type for the element that is associated with the UI Automation peer.
+ ///
+ /// The control type.
+ protected override AutomationControlType GetAutomationControlTypeCore()
+ {
+ return AutomationControlType.List;
+ }
+
+ ///
+ /// 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 Carousel
+ /// - Carousel class name
+ ///
+ protected override string GetNameCore()
+ {
+ string name = this.OwningCarousel.Name;
+ if (!string.IsNullOrEmpty(name))
+ {
+ return name;
+ }
+
+ name = AutomationProperties.GetName(this.OwningCarousel);
+ if (!string.IsNullOrEmpty(name))
+ {
+ return name;
+ }
+
+ return 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)
+ {
+ switch (patternInterface)
+ {
+ case PatternInterface.Selection:
+ return this;
+ }
+
+ return 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()
+ {
+ Carousel owner = OwningCarousel;
+
+ 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 CarouselItem element)
+ {
+ peers.Add(FromElement(element) ?? CreatePeerForElement(element));
+ }
+ }
+
+ return peers;
+ }
+ }
+}
diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/Carousel/CarouselItem.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/Carousel/CarouselItem.cs
index 5404d04c16a..ae97fee86db 100644
--- a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/Carousel/CarouselItem.cs
+++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/Carousel/CarouselItem.cs
@@ -3,8 +3,9 @@
// See the LICENSE file in the project root for more information.
using System;
+using Microsoft.Toolkit.Uwp.UI.Automation.Peers;
using Windows.UI.Xaml;
-using Windows.UI.Xaml.Controls;
+using Windows.UI.Xaml.Automation.Peers;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Input;
@@ -22,6 +23,8 @@ public partial class CarouselItem : SelectorItem
private const string SelectedState = "Selected";
private const string NormalState = "Normal";
+ private WeakReference parentCarousel;
+
///
/// Initializes a new instance of the class.
///
@@ -33,6 +36,16 @@ public CarouselItem()
RegisterPropertyChangedCallback(SelectorItem.IsSelectedProperty, OnIsSelectedChanged);
}
+ internal Carousel ParentCarousel
+ {
+ get
+ {
+ this.parentCarousel.TryGetTarget(out var carousel);
+ return carousel;
+ }
+ set => this.parentCarousel = new WeakReference(value);
+ }
+
///
protected override void OnPointerEntered(PointerRoutedEventArgs e)
{
@@ -57,6 +70,15 @@ protected override void OnPointerPressed(PointerRoutedEventArgs e)
VisualStateManager.GoToState(this, IsSelected ? PressedSelectedState : PressedState, true);
}
+ ///
+ /// Creates AutomationPeer ()
+ ///
+ /// An automation peer for this .
+ protected override AutomationPeer OnCreateAutomationPeer()
+ {
+ return new CarouselItemAutomationPeer(this);
+ }
+
internal event EventHandler Selected;
private void OnIsSelectedChanged(DependencyObject sender, DependencyProperty dp)
diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/Carousel/CarouselItemAutomationPeer.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/Carousel/CarouselItemAutomationPeer.cs
new file mode 100644
index 00000000000..ca0432d4ee7
--- /dev/null
+++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/Carousel/CarouselItemAutomationPeer.cs
@@ -0,0 +1,188 @@
+// 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 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 .
+ ///
+ public class CarouselItemAutomationPeer : FrameworkElementAutomationPeer, ISelectionItemProvider
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ ///
+ /// The that is associated with this .
+ ///
+ public CarouselItemAutomationPeer(CarouselItem owner)
+ : base(owner)
+ {
+ }
+
+ /// Gets a value indicating whether an item is selected.
+ /// True if the element is selected; otherwise, false.
+ public bool IsSelected => this.OwnerCarouselItem.IsSelected;
+
+ /// Gets the UI Automation provider that implements ISelectionProvider and acts as the container for the calling object.
+ /// The UI Automation provider.
+ public IRawElementProviderSimple SelectionContainer
+ {
+ get
+ {
+ Carousel parent = this.OwnerCarouselItem.ParentCarousel;
+ if (parent == null)
+ {
+ return null;
+ }
+
+ AutomationPeer peer = FromElement(parent);
+ return peer != null ? this.ProviderFromPeer(peer) : null;
+ }
+ }
+
+ private CarouselItem OwnerCarouselItem
+ {
+ get { return this.Owner as CarouselItem; }
+ }
+
+ /// Adds the current element to the collection of selected items.
+ public void AddToSelection()
+ {
+ CarouselItem owner = this.OwnerCarouselItem;
+ Carousel parent = owner.ParentCarousel;
+ parent?.SetSelectedItem(owner);
+ }
+
+ /// Removes the current element from the collection of selected items.
+ public void RemoveFromSelection()
+ {
+ // Cannot remove the selection of a Carousel control.
+ }
+
+ /// Clears any existing selection and then selects the current element.
+ public void Select()
+ {
+ CarouselItem owner = this.OwnerCarouselItem;
+ Carousel parent = owner.ParentCarousel;
+ parent?.SetSelectedItem(owner);
+ }
+
+ ///
+ /// Gets the control type for the element that is associated with the UI Automation peer.
+ ///
+ /// The control type.
+ protected override AutomationControlType GetAutomationControlTypeCore()
+ {
+ return AutomationControlType.ListItem;
+ }
+
+ ///
+ /// 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 CarouselItem
+ /// - Carousel class name
+ ///
+ protected override string GetNameCore()
+ {
+ string name = AutomationProperties.GetName(this.OwnerCarouselItem);
+ if (!string.IsNullOrEmpty(name))
+ {
+ return name;
+ }
+
+ name = this.OwnerCarouselItem.Name;
+ if (!string.IsNullOrEmpty(name))
+ {
+ return name;
+ }
+
+ var textBlock = this.OwnerCarouselItem.FindDescendant();
+ if (textBlock != null)
+ {
+ return textBlock.Text;
+ }
+
+ return 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)
+ {
+ switch (patternInterface)
+ {
+ case PatternInterface.SelectionItem:
+ return this;
+ }
+
+ return base.GetPatternCore(patternInterface);
+ }
+
+ ///
+ /// Returns the size of the set where the element that is associated with the automation peer is located.
+ ///
+ ///
+ /// The size of the set.
+ ///
+ protected override int GetSizeOfSetCore()
+ {
+ int sizeOfSet = base.GetSizeOfSetCore();
+
+ if (sizeOfSet != -1)
+ {
+ return sizeOfSet;
+ }
+
+ CarouselItem owner = this.OwnerCarouselItem;
+ Carousel parent = owner.ParentCarousel;
+ sizeOfSet = parent.Items.Count;
+
+ return sizeOfSet;
+ }
+
+ ///
+ /// Returns the ordinal position in the set for the element that is associated with the automation peer.
+ ///
+ ///
+ /// The ordinal position in the set.
+ ///
+ protected override int GetPositionInSetCore()
+ {
+ int positionInSet = base.GetPositionInSetCore();
+
+ if (positionInSet != -1)
+ {
+ return positionInSet;
+ }
+
+ CarouselItem owner = this.OwnerCarouselItem;
+ Carousel parent = owner.ParentCarousel;
+ positionInSet = parent.IndexFromContainer(owner) + 1;
+
+ return positionInSet;
+ }
+ }
+}
\ No newline at end of file
diff --git a/UnitTests/UnitTests.UWP/Properties/UnitTestApp.rd.xml b/UnitTests/UnitTests.UWP/Properties/UnitTestApp.rd.xml
index f89058b5081..592ef8b1d7a 100644
--- a/UnitTests/UnitTests.UWP/Properties/UnitTestApp.rd.xml
+++ b/UnitTests/UnitTests.UWP/Properties/UnitTestApp.rd.xml
@@ -37,5 +37,7 @@
+
+
\ No newline at end of file
diff --git a/UnitTests/UnitTests.UWP/UI/Controls/Test_Carousel.cs b/UnitTests/UnitTests.UWP/UI/Controls/Test_Carousel.cs
new file mode 100644
index 00000000000..227f5af8424
--- /dev/null
+++ b/UnitTests/UnitTests.UWP/UI/Controls/Test_Carousel.cs
@@ -0,0 +1,86 @@
+// 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.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_Carousel")]
+ public class Test_Carousel : VisualUITestBase
+ {
+ [TestMethod]
+ public async Task ShouldConfigureCarouselAutomationPeerAsync()
+ {
+ await App.DispatcherQueue.EnqueueAsync(async () =>
+ {
+ const int expectedSelectedIndex = 1;
+ const string expectedCarouselAutomationName = "MyAutomationPhotoItems";
+ const string expectedCarouselName = "MyPhotoItems";
+
+ var items = new ObservableCollection { new PhotoDataItem { Title = "Hello" }, new PhotoDataItem { Title = "World" } };
+
+ var carousel = new Carousel { ItemsSource = items };
+
+ await SetTestContentAsync(carousel);
+
+ // Sets the selected item to "World" from the items above.
+ carousel.SelectedIndex = expectedSelectedIndex;
+
+ var carouselAutomationPeer =
+ FrameworkElementAutomationPeer.CreatePeerForElement(carousel) as CarouselAutomationPeer;
+
+ Assert.IsNotNull(carouselAutomationPeer, "Verify that the AutomationPeer is CarouselAutomationPeer.");
+ Assert.IsFalse(carouselAutomationPeer.CanSelectMultiple, "Verify that CarouselAutomationPeer.CanSelectMultiple is false.");
+ Assert.IsTrue(carouselAutomationPeer.IsSelectionRequired, "Verify that CarouselAutomationPeer.IsSelectionRequired is true.");
+
+ // Asserts the automation peer name based on the Automation Property Name value.
+ carousel.SetValue(AutomationProperties.NameProperty, expectedCarouselAutomationName);
+ Assert.IsTrue(carouselAutomationPeer.GetName().Contains(expectedCarouselAutomationName), "Verify that the UIA name contains the given AutomationProperties.Name of the Carousel.");
+
+ // Asserts the automation peer name based on the element Name property.
+ carousel.Name = expectedCarouselName;
+ Assert.IsTrue(carouselAutomationPeer.GetName().Contains(expectedCarouselName), "Verify that the UIA name contains the given Name of the Carousel.");
+
+ var carouselItemAutomationPeers = carouselAutomationPeer.GetChildren().Cast().ToList();
+ Assert.AreEqual(items.Count, carouselItemAutomationPeers.Count);
+
+ // Asserts the default calculated position in set and size of set values
+ for (var i = 0; i < carouselItemAutomationPeers.Count; i++)
+ {
+ var peer = carouselItemAutomationPeers[i];
+ Assert.AreEqual(i + 1, peer.GetPositionInSet());
+ Assert.AreEqual(items.Count, peer.GetSizeOfSet());
+ }
+
+ // Asserts the CarouselItemAutomationPeer properties
+ var selectedItemPeer = carouselItemAutomationPeers.FirstOrDefault(peer => peer.IsSelected);
+ Assert.IsNotNull(selectedItemPeer);
+ Assert.IsTrue(selectedItemPeer.GetName().Contains(items[expectedSelectedIndex].ToString()));
+ });
+ }
+
+ public class PhotoDataItem
+ {
+ public string Title { get; set; }
+
+ public string Category { get; set; }
+
+ public string Thumbnail { 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 b8986e6452b..eb3c66e4344 100644
--- a/UnitTests/UnitTests.UWP/UnitTests.UWP.csproj
+++ b/UnitTests/UnitTests.UWP/UnitTests.UWP.csproj
@@ -194,6 +194,7 @@
+