From 0ccbedcc65d1e9635aae80d3f5238bf21cbf031e Mon Sep 17 00:00:00 2001 From: James Croft Date: Sun, 27 Sep 2020 17:19:16 +0100 Subject: [PATCH 01/14] Added automation peer class for carousel and item --- .../Carousel/Carousel.cs | 26 ++++ .../Carousel/CarouselAutomationPeer.cs | 147 ++++++++++++++++++ .../Carousel/CarouselItem.cs | 12 ++ .../Carousel/CarouselItemAutomationPeer.cs | 144 +++++++++++++++++ 4 files changed, 329 insertions(+) create mode 100644 Microsoft.Toolkit.Uwp.UI.Controls/Carousel/CarouselAutomationPeer.cs create mode 100644 Microsoft.Toolkit.Uwp.UI.Controls/Carousel/CarouselItemAutomationPeer.cs diff --git a/Microsoft.Toolkit.Uwp.UI.Controls/Carousel/Carousel.cs b/Microsoft.Toolkit.Uwp.UI.Controls/Carousel/Carousel.cs index 615ec02db67..e8d124f08ec 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls/Carousel/Carousel.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls/Carousel/Carousel.cs @@ -10,6 +10,7 @@ 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; @@ -522,6 +523,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) @@ -530,5 +542,19 @@ private void OnCarouselItemSelected(object sender, EventArgs e) SelectedItem = ItemFromContainer(item); } + + internal IEnumerable GetCarouselItems() + { + return Enumerable + .Range(0, Items.Count) + .Select(idx => (CarouselItem)ContainerFromIndex(idx)) + .Where(i => i != null); + } + + internal void SetSelectedItem(CarouselItem owner) + { + var item = ItemFromContainer(owner); + SelectedItem = item; + } } } diff --git a/Microsoft.Toolkit.Uwp.UI.Controls/Carousel/CarouselAutomationPeer.cs b/Microsoft.Toolkit.Uwp.UI.Controls/Carousel/CarouselAutomationPeer.cs new file mode 100644 index 00000000000..b41d7dec17e --- /dev/null +++ b/Microsoft.Toolkit.Uwp.UI.Controls/Carousel/CarouselAutomationPeer.cs @@ -0,0 +1,147 @@ +// 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.Linq; +using Windows.UI.Xaml.Automation.Peers; +using Windows.UI.Xaml.Automation.Provider; +using Windows.UI.Xaml.Controls; + +namespace Microsoft.Toolkit.Uwp.UI.Controls +{ + /// + /// 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 => false; + + 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() + { + CarouselItem selectedCarouselItem = OwningCarousel.GetCarouselItems().FirstOrDefault(x => x.IsSelected); + return selectedCarouselItem != null + ? 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 = base.GetNameCore(); + if (!string.IsNullOrEmpty(name)) + { + return name; + } + + if (this.OwningCarousel != null) + { + name = this.OwningCarousel.Name; + } + + if (string.IsNullOrEmpty(name)) + { + name = this.GetClassName(); + } + + return name; + } + + /// + /// 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/Carousel/CarouselItem.cs b/Microsoft.Toolkit.Uwp.UI.Controls/Carousel/CarouselItem.cs index d8e4dbe9503..51a590a61bd 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls/Carousel/CarouselItem.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls/Carousel/CarouselItem.cs @@ -4,6 +4,7 @@ using System; using Windows.UI.Xaml; +using Windows.UI.Xaml.Automation.Peers; using Windows.UI.Xaml.Controls; using Windows.UI.Xaml.Controls.Primitives; using Windows.UI.Xaml.Input; @@ -33,6 +34,8 @@ public CarouselItem() RegisterPropertyChangedCallback(SelectorItem.IsSelectedProperty, OnIsSelectedChanged); } + internal Carousel ParentCarousel { get; set; } + /// protected override void OnPointerEntered(PointerRoutedEventArgs e) { @@ -57,6 +60,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/Carousel/CarouselItemAutomationPeer.cs b/Microsoft.Toolkit.Uwp.UI.Controls/Carousel/CarouselItemAutomationPeer.cs new file mode 100644 index 00000000000..15454ca02de --- /dev/null +++ b/Microsoft.Toolkit.Uwp.UI.Controls/Carousel/CarouselItemAutomationPeer.cs @@ -0,0 +1,144 @@ +// 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.Linq; +using Windows.UI.Xaml.Automation.Peers; +using Windows.UI.Xaml.Automation.Provider; + +namespace Microsoft.Toolkit.Uwp.UI.Controls +{ + /// + /// 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() + { + CarouselItem owner = this.OwnerCarouselItem; + Carousel parent = owner.ParentCarousel; + parent.SelectedItem = null; + } + + /// 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() + { + int? index = this.OwnerCarouselItem.ParentCarousel.GetCarouselItems().ToList().IndexOf(this.OwnerCarouselItem); + + string name = base.GetNameCore(); + if (!string.IsNullOrEmpty(name)) + { + return $"{name} {index}"; + } + + if (this.OwnerCarouselItem != null && !string.IsNullOrEmpty(this.OwnerCarouselItem.Name)) + { + return this.OwnerCarouselItem.Name; + } + + if (string.IsNullOrEmpty(name)) + { + name = this.GetClassName(); + } + + return $"{name} {index}"; + } + + /// + /// 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); + } + } +} \ No newline at end of file From 36235dffb3bd4600caf2d36e5a9c473a0faf974b Mon Sep 17 00:00:00 2001 From: James Croft Date: Tue, 29 Sep 2020 21:31:23 +0100 Subject: [PATCH 02/14] Moved carousel automation peers into their own namespace --- Microsoft.Toolkit.Uwp.UI.Controls/Carousel/Carousel.cs | 2 +- .../Carousel/CarouselAutomationPeer.cs | 3 ++- Microsoft.Toolkit.Uwp.UI.Controls/Carousel/CarouselItem.cs | 2 +- .../Carousel/CarouselItemAutomationPeer.cs | 3 ++- 4 files changed, 6 insertions(+), 4 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.UI.Controls/Carousel/Carousel.cs b/Microsoft.Toolkit.Uwp.UI.Controls/Carousel/Carousel.cs index e8d124f08ec..658df535c2b 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls/Carousel/Carousel.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls/Carousel/Carousel.cs @@ -3,9 +3,9 @@ // See the LICENSE file in the project root for more information. using System; -using System.Collections; using System.Collections.Generic; using System.Linq; +using Microsoft.Toolkit.Uwp.UI.Automation.Peers; using Microsoft.Toolkit.Uwp.UI.Extensions; using Windows.Foundation; using Windows.UI.Xaml; diff --git a/Microsoft.Toolkit.Uwp.UI.Controls/Carousel/CarouselAutomationPeer.cs b/Microsoft.Toolkit.Uwp.UI.Controls/Carousel/CarouselAutomationPeer.cs index b41d7dec17e..be7ea0e651d 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls/Carousel/CarouselAutomationPeer.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls/Carousel/CarouselAutomationPeer.cs @@ -4,11 +4,12 @@ using System.Collections.Generic; using System.Linq; +using Microsoft.Toolkit.Uwp.UI.Controls; using Windows.UI.Xaml.Automation.Peers; using Windows.UI.Xaml.Automation.Provider; using Windows.UI.Xaml.Controls; -namespace Microsoft.Toolkit.Uwp.UI.Controls +namespace Microsoft.Toolkit.Uwp.UI.Automation.Peers { /// /// Defines a framework element automation peer for the control. diff --git a/Microsoft.Toolkit.Uwp.UI.Controls/Carousel/CarouselItem.cs b/Microsoft.Toolkit.Uwp.UI.Controls/Carousel/CarouselItem.cs index 51a590a61bd..7dd0690fa7a 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls/Carousel/CarouselItem.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls/Carousel/CarouselItem.cs @@ -3,9 +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.Automation.Peers; -using Windows.UI.Xaml.Controls; using Windows.UI.Xaml.Controls.Primitives; using Windows.UI.Xaml.Input; diff --git a/Microsoft.Toolkit.Uwp.UI.Controls/Carousel/CarouselItemAutomationPeer.cs b/Microsoft.Toolkit.Uwp.UI.Controls/Carousel/CarouselItemAutomationPeer.cs index 15454ca02de..7ad09f074ff 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls/Carousel/CarouselItemAutomationPeer.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls/Carousel/CarouselItemAutomationPeer.cs @@ -3,10 +3,11 @@ // See the LICENSE file in the project root for more information. using System.Linq; +using Microsoft.Toolkit.Uwp.UI.Controls; using Windows.UI.Xaml.Automation.Peers; using Windows.UI.Xaml.Automation.Provider; -namespace Microsoft.Toolkit.Uwp.UI.Controls +namespace Microsoft.Toolkit.Uwp.UI.Automation.Peers { /// /// Defines a framework element automation peer for the . From a706ca6913fc9a3e7ec96c7e6788284ef925465f Mon Sep 17 00:00:00 2001 From: James Croft Date: Wed, 11 Nov 2020 20:26:30 +0000 Subject: [PATCH 03/14] Improved implementation for Carousel automation peer --- .../Data/PhotoDataItem.cs | 5 +++++ .../Carousel/Carousel.cs | 8 -------- .../Carousel/CarouselAutomationPeer.cs | 7 +++---- .../Carousel/CarouselItem.cs | 12 +++++++++++- .../Carousel/CarouselItemAutomationPeer.cs | 14 ++++++++------ 5 files changed, 27 insertions(+), 19 deletions(-) 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/Carousel/Carousel.cs b/Microsoft.Toolkit.Uwp.UI.Controls/Carousel/Carousel.cs index 658df535c2b..d791dcd6d3f 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls/Carousel/Carousel.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls/Carousel/Carousel.cs @@ -543,14 +543,6 @@ private void OnCarouselItemSelected(object sender, EventArgs e) SelectedItem = ItemFromContainer(item); } - internal IEnumerable GetCarouselItems() - { - return Enumerable - .Range(0, Items.Count) - .Select(idx => (CarouselItem)ContainerFromIndex(idx)) - .Where(i => i != null); - } - internal void SetSelectedItem(CarouselItem owner) { var item = ItemFromContainer(owner); diff --git a/Microsoft.Toolkit.Uwp.UI.Controls/Carousel/CarouselAutomationPeer.cs b/Microsoft.Toolkit.Uwp.UI.Controls/Carousel/CarouselAutomationPeer.cs index be7ea0e651d..75029ec95d5 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls/Carousel/CarouselAutomationPeer.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls/Carousel/CarouselAutomationPeer.cs @@ -47,10 +47,9 @@ private Carousel OwningCarousel /// An array of UI Automation providers. public IRawElementProviderSimple[] GetSelection() { - CarouselItem selectedCarouselItem = OwningCarousel.GetCarouselItems().FirstOrDefault(x => x.IsSelected); - return selectedCarouselItem != null - ? new[] { this.ProviderFromPeer(FromElement(selectedCarouselItem)) } - : new IRawElementProviderSimple[] { }; + return OwningCarousel.ContainerFromItem(this.OwningCarousel.SelectedItem) is CarouselItem selectedCarouselItem + ? new[] { this.ProviderFromPeer(FromElement(selectedCarouselItem)) } + : new IRawElementProviderSimple[] { }; } /// diff --git a/Microsoft.Toolkit.Uwp.UI.Controls/Carousel/CarouselItem.cs b/Microsoft.Toolkit.Uwp.UI.Controls/Carousel/CarouselItem.cs index 7dd0690fa7a..6bf936c47d9 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls/Carousel/CarouselItem.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls/Carousel/CarouselItem.cs @@ -23,6 +23,8 @@ public class CarouselItem : SelectorItem private const string SelectedState = "Selected"; private const string NormalState = "Normal"; + private WeakReference parentCarousel; + /// /// Initializes a new instance of the class. /// @@ -34,7 +36,15 @@ public CarouselItem() RegisterPropertyChangedCallback(SelectorItem.IsSelectedProperty, OnIsSelectedChanged); } - internal Carousel ParentCarousel { get; set; } + internal Carousel ParentCarousel + { + get + { + this.parentCarousel.TryGetTarget(out var carousel); + return carousel; + } + set => this.parentCarousel = new WeakReference(value); + } /// protected override void OnPointerEntered(PointerRoutedEventArgs e) diff --git a/Microsoft.Toolkit.Uwp.UI.Controls/Carousel/CarouselItemAutomationPeer.cs b/Microsoft.Toolkit.Uwp.UI.Controls/Carousel/CarouselItemAutomationPeer.cs index 7ad09f074ff..3b95ca3c9a0 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls/Carousel/CarouselItemAutomationPeer.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls/Carousel/CarouselItemAutomationPeer.cs @@ -2,7 +2,6 @@ // 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.Linq; using Microsoft.Toolkit.Uwp.UI.Controls; using Windows.UI.Xaml.Automation.Peers; using Windows.UI.Xaml.Automation.Provider; @@ -56,7 +55,7 @@ public void AddToSelection() { CarouselItem owner = this.OwnerCarouselItem; Carousel parent = owner.ParentCarousel; - parent.SetSelectedItem(owner); + parent?.SetSelectedItem(owner); } /// Removes the current element from the collection of selected items. @@ -64,7 +63,10 @@ public void RemoveFromSelection() { CarouselItem owner = this.OwnerCarouselItem; Carousel parent = owner.ParentCarousel; - parent.SelectedItem = null; + if (parent != null) + { + parent.SelectedItem = null; + } } /// Clears any existing selection and then selects the current element. @@ -72,7 +74,7 @@ public void Select() { CarouselItem owner = this.OwnerCarouselItem; Carousel parent = owner.ParentCarousel; - parent.SetSelectedItem(owner); + parent?.SetSelectedItem(owner); } /// @@ -105,12 +107,12 @@ protected override string GetClassNameCore() /// protected override string GetNameCore() { - int? index = this.OwnerCarouselItem.ParentCarousel.GetCarouselItems().ToList().IndexOf(this.OwnerCarouselItem); + int? index = this.OwnerCarouselItem.ParentCarousel?.IndexFromContainer(this.OwnerCarouselItem); string name = base.GetNameCore(); if (!string.IsNullOrEmpty(name)) { - return $"{name} {index}"; + return $"{name}"; } if (this.OwnerCarouselItem != null && !string.IsNullOrEmpty(this.OwnerCarouselItem.Name)) From 411e39d0c557e2dc9de87b49507d5697c964175b Mon Sep 17 00:00:00 2001 From: James Croft Date: Wed, 18 Nov 2020 19:11:47 +0000 Subject: [PATCH 04/14] Added test for Carousel automation peer. --- .../Carousel/CarouselAutomationPeer.cs | 11 ++-- .../UI/Controls/Test_Carousel.cs | 52 +++++++++++++++++++ UnitTests/UnitTests.UWP/UnitTests.UWP.csproj | 1 + 3 files changed, 59 insertions(+), 5 deletions(-) create mode 100644 UnitTests/UnitTests.UWP/UI/Controls/Test_Carousel.cs diff --git a/Microsoft.Toolkit.Uwp.UI.Controls/Carousel/CarouselAutomationPeer.cs b/Microsoft.Toolkit.Uwp.UI.Controls/Carousel/CarouselAutomationPeer.cs index 75029ec95d5..9fb17f666e3 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls/Carousel/CarouselAutomationPeer.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls/Carousel/CarouselAutomationPeer.cs @@ -82,17 +82,18 @@ protected override string GetClassNameCore() /// protected override string GetNameCore() { - string name = base.GetNameCore(); - if (!string.IsNullOrEmpty(name)) - { - return name; - } + string name = string.Empty; if (this.OwningCarousel != null) { name = this.OwningCarousel.Name; } + if (string.IsNullOrEmpty(name)) + { + name = base.GetNameCore(); + } + if (string.IsNullOrEmpty(name)) { name = this.GetClassName(); 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..f1303643a51 --- /dev/null +++ b/UnitTests/UnitTests.UWP/UI/Controls/Test_Carousel.cs @@ -0,0 +1,52 @@ +// 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.Diagnostics; +using System.Linq; +using Microsoft.Toolkit.Uwp.UI.Controls; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Microsoft.VisualStudio.TestTools.UnitTesting.AppContainer; +using Windows.UI.Xaml.Automation.Peers; +using Microsoft.Toolkit.Uwp.UI.Automation.Peers; + +namespace UnitTests.UWP.UI.Controls +{ + [TestClass] + [TestCategory("Test_Carousel")] + public class Test_Carousel + { + [UITestMethod] + public void ShouldConfigureCarouselAutomationPeer() + { + var items = new ObservableCollection { new PhotoDataItem { Title = "Hello" }, new PhotoDataItem { Title = "World" } }; + var carousel = new Carousel { ItemsSource = items }; + + var carouselPeer = FrameworkElementAutomationPeer.CreatePeerForElement(carousel) as CarouselAutomationPeer; + + Assert.IsNotNull(carouselPeer, "Verify that the AutomationPeer is CarouselAutomationPeer."); + Assert.IsFalse(carouselPeer.CanSelectMultiple, "Verify that CarouselAutomationPeer.CanSelectMultiple is false."); + Assert.IsFalse(carouselPeer.IsSelectionRequired, "Verify that CarouselAutomationPeer.IsSelectionRequired is false."); + + Assert.IsTrue(carouselPeer.GetName().Contains(nameof(Carousel)), "Verify that the UIA name contains the class name of the Carousel."); + carousel.Name = "TextItems"; + Assert.IsTrue(carouselPeer.GetName().Contains(carousel.Name), "Verify that the UIA name contains the given name of the Carousel."); + } + + 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 a05bbb4e8e3..fb23e832fb9 100644 --- a/UnitTests/UnitTests.UWP/UnitTests.UWP.csproj +++ b/UnitTests/UnitTests.UWP/UnitTests.UWP.csproj @@ -179,6 +179,7 @@ + From b959fbfc42120879489404866a51c4dd8ddb8310 Mon Sep 17 00:00:00 2001 From: James Croft Date: Sun, 22 Nov 2020 15:18:27 +0000 Subject: [PATCH 05/14] Updated carousel automation peer to improve returned name, and selection required being true --- .../Carousel/CarouselAutomationPeer.cs | 23 ++++++++++--------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.UI.Controls/Carousel/CarouselAutomationPeer.cs b/Microsoft.Toolkit.Uwp.UI.Controls/Carousel/CarouselAutomationPeer.cs index 9fb17f666e3..609bf3138ad 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls/Carousel/CarouselAutomationPeer.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls/Carousel/CarouselAutomationPeer.cs @@ -3,8 +3,8 @@ // See the LICENSE file in the project root for more information. using System.Collections.Generic; -using System.Linq; 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; @@ -33,7 +33,7 @@ public CarouselAutomationPeer(Carousel owner) /// 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 => false; + public bool IsSelectionRequired => true; private Carousel OwningCarousel { @@ -82,24 +82,25 @@ protected override string GetClassNameCore() /// protected override string GetNameCore() { - string name = string.Empty; - - if (this.OwningCarousel != null) + string name = AutomationProperties.GetName(this.OwningCarousel); + if (!string.IsNullOrEmpty(name)) { - name = this.OwningCarousel.Name; + return name; } - if (string.IsNullOrEmpty(name)) + name = this.OwningCarousel.Name; + if (!string.IsNullOrEmpty(name)) { - name = base.GetNameCore(); + return name; } - if (string.IsNullOrEmpty(name)) + name = base.GetNameCore(); + if (!string.IsNullOrEmpty(name)) { - name = this.GetClassName(); + return name; } - return name; + return string.Empty; } /// From 631f6b2dedb845d5dbd566908a790fc7a9d7902c Mon Sep 17 00:00:00 2001 From: James Croft Date: Sun, 22 Nov 2020 15:19:52 +0000 Subject: [PATCH 06/14] Updated carousel item automation peer to remove ability to remove selection, improved returned name, automation ID, and added size of set and position in set methods. --- .../Carousel/CarouselItemAutomationPeer.cs | 98 ++++++++++++++++--- 1 file changed, 84 insertions(+), 14 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.UI.Controls/Carousel/CarouselItemAutomationPeer.cs b/Microsoft.Toolkit.Uwp.UI.Controls/Carousel/CarouselItemAutomationPeer.cs index 3b95ca3c9a0..416cf43d3c6 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls/Carousel/CarouselItemAutomationPeer.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls/Carousel/CarouselItemAutomationPeer.cs @@ -3,8 +3,11 @@ // See the LICENSE file in the project root for more information. using Microsoft.Toolkit.Uwp.UI.Controls; +using Microsoft.Toolkit.Uwp.UI.Extensions; +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 { @@ -61,12 +64,7 @@ public void AddToSelection() /// Removes the current element from the collection of selected items. public void RemoveFromSelection() { - CarouselItem owner = this.OwnerCarouselItem; - Carousel parent = owner.ParentCarousel; - if (parent != null) - { - parent.SelectedItem = null; - } + // Cannot remove the selection of a Carousel control. } /// Clears any existing selection and then selects the current element. @@ -107,25 +105,53 @@ protected override string GetClassNameCore() /// protected override string GetNameCore() { - int? index = this.OwnerCarouselItem.ParentCarousel?.IndexFromContainer(this.OwnerCarouselItem); + string name = AutomationProperties.GetName(this.OwnerCarouselItem); + if (!string.IsNullOrEmpty(name)) + { + return name; + } - string name = base.GetNameCore(); + name = this.OwnerCarouselItem.Name; if (!string.IsNullOrEmpty(name)) { - return $"{name}"; + return name; } - if (this.OwnerCarouselItem != null && !string.IsNullOrEmpty(this.OwnerCarouselItem.Name)) + var textBlock = this.OwnerCarouselItem.FindDescendant(); + if (textBlock != null) { - return this.OwnerCarouselItem.Name; + return textBlock.Name; } - if (string.IsNullOrEmpty(name)) + name = base.GetNameCore(); + if (!string.IsNullOrEmpty(name)) { - name = this.GetClassName(); + return name; } - return $"{name} {index}"; + 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() + { + var automationId = base.GetAutomationIdCore(); + if (!string.IsNullOrEmpty(automationId)) + { + return automationId; + } + + if (this.OwnerCarouselItem != null) + { + return this.GetNameCore(); + } + + return string.Empty; } /// @@ -143,5 +169,49 @@ protected override object GetPatternCore(PatternInterface patternInterface) 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); + + return positionInSet; + } } } \ No newline at end of file From ba3c81f190a3bc8a230ee825fa3d099e471b4c7d Mon Sep 17 00:00:00 2001 From: James Croft Date: Sun, 22 Nov 2020 15:22:10 +0000 Subject: [PATCH 07/14] Fixed TextBlock to text property in automation peer --- .../Carousel/CarouselItemAutomationPeer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Microsoft.Toolkit.Uwp.UI.Controls/Carousel/CarouselItemAutomationPeer.cs b/Microsoft.Toolkit.Uwp.UI.Controls/Carousel/CarouselItemAutomationPeer.cs index 416cf43d3c6..fc5a230c3ef 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls/Carousel/CarouselItemAutomationPeer.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls/Carousel/CarouselItemAutomationPeer.cs @@ -120,7 +120,7 @@ protected override string GetNameCore() var textBlock = this.OwnerCarouselItem.FindDescendant(); if (textBlock != null) { - return textBlock.Name; + return textBlock.Text; } name = base.GetNameCore(); From f42f236f5cf5349bd9d76dc402da4daeda7dfe80 Mon Sep 17 00:00:00 2001 From: James Croft Date: Sun, 22 Nov 2020 18:47:57 +0000 Subject: [PATCH 08/14] Fixed carousel automation tests --- .../Carousel/CarouselAutomationPeer.cs | 4 ++-- .../UI/Controls/Test_Carousel.cs | 20 ++++++++++++------- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.UI.Controls/Carousel/CarouselAutomationPeer.cs b/Microsoft.Toolkit.Uwp.UI.Controls/Carousel/CarouselAutomationPeer.cs index 609bf3138ad..e3ce729e611 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls/Carousel/CarouselAutomationPeer.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls/Carousel/CarouselAutomationPeer.cs @@ -82,13 +82,13 @@ protected override string GetClassNameCore() /// protected override string GetNameCore() { - string name = AutomationProperties.GetName(this.OwningCarousel); + string name = this.OwningCarousel.Name; if (!string.IsNullOrEmpty(name)) { return name; } - name = this.OwningCarousel.Name; + name = AutomationProperties.GetName(this.OwningCarousel); if (!string.IsNullOrEmpty(name)) { return name; diff --git a/UnitTests/UnitTests.UWP/UI/Controls/Test_Carousel.cs b/UnitTests/UnitTests.UWP/UI/Controls/Test_Carousel.cs index f1303643a51..d365c5c1cc0 100644 --- a/UnitTests/UnitTests.UWP/UI/Controls/Test_Carousel.cs +++ b/UnitTests/UnitTests.UWP/UI/Controls/Test_Carousel.cs @@ -6,6 +6,7 @@ using System.Collections.ObjectModel; using System.Diagnostics; using System.Linq; +using Windows.UI.Xaml.Automation; using Microsoft.Toolkit.Uwp.UI.Controls; using Microsoft.VisualStudio.TestTools.UnitTesting; using Microsoft.VisualStudio.TestTools.UnitTesting.AppContainer; @@ -21,18 +22,23 @@ public class Test_Carousel [UITestMethod] public void ShouldConfigureCarouselAutomationPeer() { + const string automationName = "MyAutomationPhotoItems"; + const string name = "MyPhotoItems"; + var items = new ObservableCollection { new PhotoDataItem { Title = "Hello" }, new PhotoDataItem { Title = "World" } }; var carousel = new Carousel { ItemsSource = items }; - var carouselPeer = FrameworkElementAutomationPeer.CreatePeerForElement(carousel) as CarouselAutomationPeer; + 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 false."); - Assert.IsNotNull(carouselPeer, "Verify that the AutomationPeer is CarouselAutomationPeer."); - Assert.IsFalse(carouselPeer.CanSelectMultiple, "Verify that CarouselAutomationPeer.CanSelectMultiple is false."); - Assert.IsFalse(carouselPeer.IsSelectionRequired, "Verify that CarouselAutomationPeer.IsSelectionRequired is false."); + carousel.SetValue(AutomationProperties.NameProperty, automationName); + Assert.IsTrue(carouselAutomationPeer.GetName().Contains(automationName), "Verify that the UIA name contains the given AutomationProperties.Name of the Carousel."); - Assert.IsTrue(carouselPeer.GetName().Contains(nameof(Carousel)), "Verify that the UIA name contains the class name of the Carousel."); - carousel.Name = "TextItems"; - Assert.IsTrue(carouselPeer.GetName().Contains(carousel.Name), "Verify that the UIA name contains the given name of the Carousel."); + carousel.Name = name; + Assert.IsTrue(carouselAutomationPeer.GetName().Contains(name), "Verify that the UIA name contains the given Name of the Carousel."); } public class PhotoDataItem From 41491ee4f37fa2bc1f9d911c3ed6f48510138183 Mon Sep 17 00:00:00 2001 From: James Croft Date: Mon, 7 Dec 2020 19:15:45 +0000 Subject: [PATCH 09/14] Altered GetNameCore to default to return the base implementation when alternative options don't return results. --- .../Carousel/CarouselAutomationPeer.cs | 8 +------- .../Carousel/CarouselItemAutomationPeer.cs | 10 ++-------- 2 files changed, 3 insertions(+), 15 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.UI.Controls/Carousel/CarouselAutomationPeer.cs b/Microsoft.Toolkit.Uwp.UI.Controls/Carousel/CarouselAutomationPeer.cs index e3ce729e611..59a310ec3b4 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls/Carousel/CarouselAutomationPeer.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls/Carousel/CarouselAutomationPeer.cs @@ -94,13 +94,7 @@ protected override string GetNameCore() return name; } - name = base.GetNameCore(); - if (!string.IsNullOrEmpty(name)) - { - return name; - } - - return string.Empty; + return base.GetNameCore(); } /// diff --git a/Microsoft.Toolkit.Uwp.UI.Controls/Carousel/CarouselItemAutomationPeer.cs b/Microsoft.Toolkit.Uwp.UI.Controls/Carousel/CarouselItemAutomationPeer.cs index fc5a230c3ef..53e8244b2aa 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls/Carousel/CarouselItemAutomationPeer.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls/Carousel/CarouselItemAutomationPeer.cs @@ -123,13 +123,7 @@ protected override string GetNameCore() return textBlock.Text; } - name = base.GetNameCore(); - if (!string.IsNullOrEmpty(name)) - { - return name; - } - - return string.Empty; + return base.GetNameCore(); } /// @@ -209,7 +203,7 @@ protected override int GetPositionInSetCore() CarouselItem owner = this.OwnerCarouselItem; Carousel parent = owner.ParentCarousel; - positionInSet = parent.IndexFromContainer(owner); + positionInSet = parent.IndexFromContainer(owner) + 1; return positionInSet; } From 4dbba32b322bae714b6e1b785f4ef2069c8db4f6 Mon Sep 17 00:00:00 2001 From: James Croft Date: Thu, 18 Feb 2021 09:14:27 +0000 Subject: [PATCH 10/14] Updated Carousel tests to include new Visual UI test implementation. --- .../Carousel/CarouselAutomationPeer.cs | 0 .../Carousel/CarouselItemAutomationPeer.cs | 0 .../UI/Controls/Test_Carousel.cs | 101 ++++++++++++------ 3 files changed, 71 insertions(+), 30 deletions(-) rename {Microsoft.Toolkit.Uwp.UI.Controls => Microsoft.Toolkit.Uwp.UI.Controls.Layout}/Carousel/CarouselAutomationPeer.cs (100%) rename {Microsoft.Toolkit.Uwp.UI.Controls => Microsoft.Toolkit.Uwp.UI.Controls.Layout}/Carousel/CarouselItemAutomationPeer.cs (100%) diff --git a/Microsoft.Toolkit.Uwp.UI.Controls/Carousel/CarouselAutomationPeer.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/Carousel/CarouselAutomationPeer.cs similarity index 100% rename from Microsoft.Toolkit.Uwp.UI.Controls/Carousel/CarouselAutomationPeer.cs rename to Microsoft.Toolkit.Uwp.UI.Controls.Layout/Carousel/CarouselAutomationPeer.cs diff --git a/Microsoft.Toolkit.Uwp.UI.Controls/Carousel/CarouselItemAutomationPeer.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/Carousel/CarouselItemAutomationPeer.cs similarity index 100% rename from Microsoft.Toolkit.Uwp.UI.Controls/Carousel/CarouselItemAutomationPeer.cs rename to Microsoft.Toolkit.Uwp.UI.Controls.Layout/Carousel/CarouselItemAutomationPeer.cs diff --git a/UnitTests/UnitTests.UWP/UI/Controls/Test_Carousel.cs b/UnitTests/UnitTests.UWP/UI/Controls/Test_Carousel.cs index d365c5c1cc0..18187fcc566 100644 --- a/UnitTests/UnitTests.UWP/UI/Controls/Test_Carousel.cs +++ b/UnitTests/UnitTests.UWP/UI/Controls/Test_Carousel.cs @@ -2,57 +2,98 @@ // 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.Diagnostics; using System.Linq; +using System.Threading.Tasks; using Windows.UI.Xaml.Automation; -using Microsoft.Toolkit.Uwp.UI.Controls; using Microsoft.VisualStudio.TestTools.UnitTesting; -using Microsoft.VisualStudio.TestTools.UnitTesting.AppContainer; using Windows.UI.Xaml.Automation.Peers; +using Windows.UI.Xaml.Controls; +using Windows.UI.Xaml.Markup; using Microsoft.Toolkit.Uwp.UI.Automation.Peers; +using Microsoft.Toolkit.Uwp.Extensions; +using Carousel = Microsoft.Toolkit.Uwp.UI.Controls.Carousel; namespace UnitTests.UWP.UI.Controls { [TestClass] [TestCategory("Test_Carousel")] - public class Test_Carousel + public class Test_Carousel : VisualUITestBase { - [UITestMethod] - public void ShouldConfigureCarouselAutomationPeer() + [TestMethod] + public async Task ShouldConfigureCarouselAutomationPeerAsync() { - const string automationName = "MyAutomationPhotoItems"; - const string name = "MyPhotoItems"; + await App.DispatcherQueue.EnqueueAsync(async () => + { + var treeRoot = XamlReader.Load(@" + + + + + - var items = new ObservableCollection { new PhotoDataItem { Title = "Hello" }, new PhotoDataItem { Title = "World" } }; - var carousel = new Carousel { ItemsSource = items }; + + Hello + + + World + + + +") as Page; - var carouselAutomationPeer = FrameworkElementAutomationPeer.CreatePeerForElement(carousel) as CarouselAutomationPeer; + // This is based on the XAML above. + var expectedNumItems = 2; + var expectedSelectedItem = "World"; - 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 false."); + Assert.IsNotNull(treeRoot, "XAML for test failed to load"); - carousel.SetValue(AutomationProperties.NameProperty, automationName); - Assert.IsTrue(carouselAutomationPeer.GetName().Contains(automationName), "Verify that the UIA name contains the given AutomationProperties.Name of the Carousel."); + await SetTestContentAsync(treeRoot); - carousel.Name = name; - Assert.IsTrue(carouselAutomationPeer.GetName().Contains(name), "Verify that the UIA name contains the given Name of the Carousel."); - } + var outerGrid = treeRoot.Content as Grid; - public class PhotoDataItem - { - public string Title { get; set; } + Assert.IsNotNull(outerGrid, "Couldn't find Page content."); - public string Category { get; set; } + var carousel = outerGrid.Children.FirstOrDefault() as Carousel; - public string Thumbnail { get; set; } + Assert.IsNotNull(carousel, "Couldn't find Target Carousel."); - public override string ToString() - { - return Title; - } + // Sets the selected item to "World" from the XAML above. + carousel.SelectedIndex = 1; + + const string automationName = "MyAutomationPhotoItems"; + const string name = "MyPhotoItems"; + + 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."); + + carousel.SetValue(AutomationProperties.NameProperty, automationName); + Assert.IsTrue(carouselAutomationPeer.GetName().Contains(automationName), "Verify that the UIA name contains the given AutomationProperties.Name of the Carousel."); + + carousel.Name = name; + Assert.IsTrue(carouselAutomationPeer.GetName().Contains(name), "Verify that the UIA name contains the given Name of the Carousel."); + + var carouselItemAutomationPeers = carouselAutomationPeer.GetChildren().Cast().ToList(); + Assert.AreEqual(expectedNumItems, carouselItemAutomationPeers.Count); + + for (var i = 0; i < carouselItemAutomationPeers.Count; i++) + { + var peer = carouselItemAutomationPeers[i]; + Assert.AreEqual(i + 1, peer.GetPositionInSet()); + Assert.AreEqual(expectedNumItems, peer.GetSizeOfSet()); + } + + var selected = carouselItemAutomationPeers.FirstOrDefault(peer => peer.IsSelected); + Assert.IsNotNull(selected); + Assert.IsTrue(selected.GetName().Contains(expectedSelectedItem)); + }); } } } \ No newline at end of file From ddea9ce551a2f2c4cadc97da4349d3145295243c Mon Sep 17 00:00:00 2001 From: James Croft Date: Thu, 18 Feb 2021 17:12:46 +0000 Subject: [PATCH 11/14] Updated carousel test to not use parsed XAML string --- .../UI/Controls/Test_Carousel.cs | 62 +++++++------------ 1 file changed, 23 insertions(+), 39 deletions(-) diff --git a/UnitTests/UnitTests.UWP/UI/Controls/Test_Carousel.cs b/UnitTests/UnitTests.UWP/UI/Controls/Test_Carousel.cs index 18187fcc566..fd47907046b 100644 --- a/UnitTests/UnitTests.UWP/UI/Controls/Test_Carousel.cs +++ b/UnitTests/UnitTests.UWP/UI/Controls/Test_Carousel.cs @@ -2,6 +2,7 @@ // 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; @@ -24,45 +25,14 @@ public async Task ShouldConfigureCarouselAutomationPeerAsync() { await App.DispatcherQueue.EnqueueAsync(async () => { - var treeRoot = XamlReader.Load(@" - - - - - + const int selectedIndex = 1; + var items = new ObservableCollection { new PhotoDataItem { Title = "Hello" }, new PhotoDataItem { Title = "World" } }; + var carousel = new Carousel { ItemsSource = items }; - - Hello - - - World - - - -") as Page; - - // This is based on the XAML above. - var expectedNumItems = 2; - var expectedSelectedItem = "World"; - - Assert.IsNotNull(treeRoot, "XAML for test failed to load"); - - await SetTestContentAsync(treeRoot); - - var outerGrid = treeRoot.Content as Grid; - - Assert.IsNotNull(outerGrid, "Couldn't find Page content."); - - var carousel = outerGrid.Children.FirstOrDefault() as Carousel; - - Assert.IsNotNull(carousel, "Couldn't find Target Carousel."); + await SetTestContentAsync(carousel); // Sets the selected item to "World" from the XAML above. - carousel.SelectedIndex = 1; + carousel.SelectedIndex = selectedIndex; const string automationName = "MyAutomationPhotoItems"; const string name = "MyPhotoItems"; @@ -81,19 +51,33 @@ await App.DispatcherQueue.EnqueueAsync(async () => Assert.IsTrue(carouselAutomationPeer.GetName().Contains(name), "Verify that the UIA name contains the given Name of the Carousel."); var carouselItemAutomationPeers = carouselAutomationPeer.GetChildren().Cast().ToList(); - Assert.AreEqual(expectedNumItems, carouselItemAutomationPeers.Count); + Assert.AreEqual(items.Count, carouselItemAutomationPeers.Count); for (var i = 0; i < carouselItemAutomationPeers.Count; i++) { var peer = carouselItemAutomationPeers[i]; Assert.AreEqual(i + 1, peer.GetPositionInSet()); - Assert.AreEqual(expectedNumItems, peer.GetSizeOfSet()); + Assert.AreEqual(items.Count, peer.GetSizeOfSet()); } var selected = carouselItemAutomationPeers.FirstOrDefault(peer => peer.IsSelected); Assert.IsNotNull(selected); - Assert.IsTrue(selected.GetName().Contains(expectedSelectedItem)); + Assert.IsTrue(selected.GetName().Contains(items[selectedIndex].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 From 79ea8666e4ea6ed9018a9220da9b521f03c9bada Mon Sep 17 00:00:00 2001 From: James Croft Date: Thu, 18 Feb 2021 19:18:23 +0000 Subject: [PATCH 12/14] Added change to rd.xml and cleaned up carousel test --- .../Properties/UnitTestApp.rd.xml | 2 + .../UI/Controls/Test_Carousel.cs | 37 ++++++++++--------- 2 files changed, 22 insertions(+), 17 deletions(-) diff --git a/UnitTests/UnitTests.UWP/Properties/UnitTestApp.rd.xml b/UnitTests/UnitTests.UWP/Properties/UnitTestApp.rd.xml index 50999352cde..597030354d7 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 index fd47907046b..a8257c20003 100644 --- a/UnitTests/UnitTests.UWP/UI/Controls/Test_Carousel.cs +++ b/UnitTests/UnitTests.UWP/UI/Controls/Test_Carousel.cs @@ -8,11 +8,9 @@ using Windows.UI.Xaml.Automation; using Microsoft.VisualStudio.TestTools.UnitTesting; using Windows.UI.Xaml.Automation.Peers; -using Windows.UI.Xaml.Controls; -using Windows.UI.Xaml.Markup; using Microsoft.Toolkit.Uwp.UI.Automation.Peers; using Microsoft.Toolkit.Uwp.Extensions; -using Carousel = Microsoft.Toolkit.Uwp.UI.Controls.Carousel; +using Microsoft.Toolkit.Uwp.UI.Controls; namespace UnitTests.UWP.UI.Controls { @@ -25,18 +23,19 @@ public async Task ShouldConfigureCarouselAutomationPeerAsync() { await App.DispatcherQueue.EnqueueAsync(async () => { - const int selectedIndex = 1; + 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 XAML above. - carousel.SelectedIndex = selectedIndex; - - const string automationName = "MyAutomationPhotoItems"; - const string name = "MyPhotoItems"; - + // Sets the selected item to "World" from the items above. + carousel.SelectedIndex = expectedSelectedIndex; + var carouselAutomationPeer = FrameworkElementAutomationPeer.CreatePeerForElement(carousel) as CarouselAutomationPeer; @@ -44,15 +43,18 @@ await App.DispatcherQueue.EnqueueAsync(async () => Assert.IsFalse(carouselAutomationPeer.CanSelectMultiple, "Verify that CarouselAutomationPeer.CanSelectMultiple is false."); Assert.IsTrue(carouselAutomationPeer.IsSelectionRequired, "Verify that CarouselAutomationPeer.IsSelectionRequired is true."); - carousel.SetValue(AutomationProperties.NameProperty, automationName); - Assert.IsTrue(carouselAutomationPeer.GetName().Contains(automationName), "Verify that the UIA name contains the given AutomationProperties.Name of the Carousel."); + // 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."); - carousel.Name = name; - Assert.IsTrue(carouselAutomationPeer.GetName().Contains(name), "Verify that the UIA name contains the given 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]; @@ -60,9 +62,10 @@ await App.DispatcherQueue.EnqueueAsync(async () => Assert.AreEqual(items.Count, peer.GetSizeOfSet()); } - var selected = carouselItemAutomationPeers.FirstOrDefault(peer => peer.IsSelected); - Assert.IsNotNull(selected); - Assert.IsTrue(selected.GetName().Contains(items[selectedIndex].ToString())); + // Asserts the CarouselItemAutomationPeer properties + var selectedItemPeer = carouselItemAutomationPeers.FirstOrDefault(peer => peer.IsSelected); + Assert.IsNotNull(selectedItemPeer); + Assert.IsTrue(selectedItemPeer.GetName().Contains(items[expectedSelectedIndex].ToString())); }); } From 32b015dc7b41cb84d69421ca10e4de3a91bfa98c Mon Sep 17 00:00:00 2001 From: James Croft Date: Wed, 24 Feb 2021 18:05:36 +0000 Subject: [PATCH 13/14] Removed GetAutomationIdCore implementation from CarouselItem and BladeItem automation peers --- .../BladeView/BladeItemAutomationPeer.cs | 22 ------------------- .../Carousel/CarouselItemAutomationPeer.cs | 22 ------------------- 2 files changed, 44 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/BladeView/BladeItemAutomationPeer.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/BladeView/BladeItemAutomationPeer.cs index 57033555a37..6e3267914cd 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/BladeView/BladeItemAutomationPeer.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/BladeView/BladeItemAutomationPeer.cs @@ -94,28 +94,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/CarouselItemAutomationPeer.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/Carousel/CarouselItemAutomationPeer.cs index 53e8244b2aa..5706b8cbb9b 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/Carousel/CarouselItemAutomationPeer.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/Carousel/CarouselItemAutomationPeer.cs @@ -126,28 +126,6 @@ protected override string GetNameCore() return base.GetNameCore(); } - /// - /// 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() - { - var automationId = base.GetAutomationIdCore(); - if (!string.IsNullOrEmpty(automationId)) - { - return automationId; - } - - if (this.OwnerCarouselItem != null) - { - return this.GetNameCore(); - } - - return string.Empty; - } - /// /// Gets the control pattern that is associated with the specified Windows.UI.Xaml.Automation.Peers.PatternInterface. /// From 7547a4fb98526cd644369ed9844c0e2173f35b4f Mon Sep 17 00:00:00 2001 From: James Croft Date: Wed, 24 Feb 2021 19:23:28 +0000 Subject: [PATCH 14/14] Removed using directives which no longer exist --- Microsoft.Toolkit.Uwp.UI.Controls.Layout/Carousel/Carousel.cs | 1 - .../Carousel/CarouselItemAutomationPeer.cs | 1 - UnitTests/UnitTests.UWP/UI/Controls/Test_Carousel.cs | 2 +- 3 files changed, 1 insertion(+), 3 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/Carousel/Carousel.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/Carousel/Carousel.cs index 5df18a37ef9..ababf502fe3 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/Carousel/Carousel.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/Carousel/Carousel.cs @@ -6,7 +6,6 @@ using System.Collections.Generic; using System.Linq; using Microsoft.Toolkit.Uwp.UI.Automation.Peers; -using Microsoft.Toolkit.Uwp.UI.Extensions; using Windows.Foundation; using Windows.UI.Xaml; using Windows.UI.Xaml.Automation; diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/Carousel/CarouselItemAutomationPeer.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/Carousel/CarouselItemAutomationPeer.cs index 5706b8cbb9b..ca0432d4ee7 100644 --- a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/Carousel/CarouselItemAutomationPeer.cs +++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/Carousel/CarouselItemAutomationPeer.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using Microsoft.Toolkit.Uwp.UI.Controls; -using Microsoft.Toolkit.Uwp.UI.Extensions; using Windows.UI.Xaml.Automation; using Windows.UI.Xaml.Automation.Peers; using Windows.UI.Xaml.Automation.Provider; diff --git a/UnitTests/UnitTests.UWP/UI/Controls/Test_Carousel.cs b/UnitTests/UnitTests.UWP/UI/Controls/Test_Carousel.cs index a8257c20003..227f5af8424 100644 --- a/UnitTests/UnitTests.UWP/UI/Controls/Test_Carousel.cs +++ b/UnitTests/UnitTests.UWP/UI/Controls/Test_Carousel.cs @@ -8,8 +8,8 @@ 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.Extensions; using Microsoft.Toolkit.Uwp.UI.Controls; namespace UnitTests.UWP.UI.Controls