From 520f3d42c156a6a4ddcba18b2cd626f9326011f8 Mon Sep 17 00:00:00 2001 From: Vladislav Antonyuk Date: Fri, 24 Nov 2023 11:28:45 +0200 Subject: [PATCH 1/6] IndicatorView IndicatorTemplate Binding --- .../Controls.Sample/Pages/Controls/IndicatorPage.xaml | 9 ++++++++- .../src/Core/IndicatorView/IndicatorStackLayout.cs | 3 +++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/Controls/samples/Controls.Sample/Pages/Controls/IndicatorPage.xaml b/src/Controls/samples/Controls.Sample/Pages/Controls/IndicatorPage.xaml index 92dce624ad2d..5b9ae2c63a9c 100644 --- a/src/Controls/samples/Controls.Sample/Pages/Controls/IndicatorPage.xaml +++ b/src/Controls/samples/Controls.Sample/Pages/Controls/IndicatorPage.xaml @@ -121,7 +121,14 @@ x:Name="indicatorView" IndicatorColor="LightGray" SelectedIndicatorColor="DarkGray" - HorizontalOptions="Center" /> + HorizontalOptions="Center"> + + + + + diff --git a/src/Controls/src/Core/IndicatorView/IndicatorStackLayout.cs b/src/Controls/src/Core/IndicatorView/IndicatorStackLayout.cs index 296e2816dceb..55c9b5090fe2 100644 --- a/src/Controls/src/Core/IndicatorView/IndicatorStackLayout.cs +++ b/src/Controls/src/Core/IndicatorView/IndicatorStackLayout.cs @@ -3,6 +3,7 @@ using System.Collections; using System.Collections.Specialized; using System.ComponentModel; +using System.Linq; using Microsoft.Maui.Graphics; namespace Microsoft.Maui.Controls @@ -125,6 +126,7 @@ void AddExtraIndicatorItems() var indicatorTemplate = _indicatorView.IndicatorTemplate; var oldCount = Children.Count; + var items = _indicatorView.ItemsSource.Cast().ToArray(); for (var i = 0; i < indicatorCount - oldCount && i < indicatorMaximumVisible - oldCount; i++) { var size = indicatorSize > 0 ? indicatorSize : 10; @@ -139,6 +141,7 @@ void AddExtraIndicatorItems() HeightRequest = size, CornerRadius = _indicatorView.IndicatorsShape == IndicatorShape.Circle ? (float)size / 2 : 0 }; + indicator.BindingContext = items[i]; var tapGestureRecognizer = new TapGestureRecognizer(); tapGestureRecognizer.Tapped += (sender, args) => _indicatorView.Position = Children.IndexOf(sender as View); indicator.GestureRecognizers.Add(tapGestureRecognizer); From 5dbf3352cc48de6f0ae187e1de5bf9f36e468eb1 Mon Sep 17 00:00:00 2001 From: Vladislav Antonyuk Date: Fri, 16 Feb 2024 00:31:44 +0200 Subject: [PATCH 2/6] Update to use BindableLayout --- .../IndicatorView/IndicatorStackLayout.cs | 81 +++++++++---------- 1 file changed, 37 insertions(+), 44 deletions(-) diff --git a/src/Controls/src/Core/IndicatorView/IndicatorStackLayout.cs b/src/Controls/src/Core/IndicatorView/IndicatorStackLayout.cs index 55c9b5090fe2..3c90aa45623e 100644 --- a/src/Controls/src/Core/IndicatorView/IndicatorStackLayout.cs +++ b/src/Controls/src/Core/IndicatorView/IndicatorStackLayout.cs @@ -1,16 +1,12 @@ -#nullable disable -using System; -using System.Collections; -using System.Collections.Specialized; using System.ComponentModel; -using System.Linq; +using Microsoft.Maui.Controls.Shapes; using Microsoft.Maui.Graphics; namespace Microsoft.Maui.Controls { internal class IndicatorStackLayout : StackLayout { - IndicatorView _indicatorView; + readonly IndicatorView _indicatorView; public IndicatorStackLayout(IndicatorView indicatorView) { _indicatorView = indicatorView; @@ -18,7 +14,19 @@ public IndicatorStackLayout(IndicatorView indicatorView) _indicatorView.PropertyChanged += _indicatorViewPropertyChanged; } - void _indicatorViewPropertyChanged(object sender, PropertyChangedEventArgs e) + protected override void OnChildAdded(Element child) + { + base.OnChildAdded(child); + + if (child is View view) + { + var tapGestureRecognizer = new TapGestureRecognizer(); + tapGestureRecognizer.Tapped += (sender, _) => _indicatorView.Position = Children.IndexOf(sender as View); + view.GestureRecognizers.Add(tapGestureRecognizer); + } + } + + void _indicatorViewPropertyChanged(object? sender, PropertyChangedEventArgs e) { if (e.PropertyName == IndicatorView.IndicatorsShapeProperty.PropertyName || e.PropertyName == IndicatorView.IndicatorTemplateProperty.PropertyName) @@ -54,7 +62,6 @@ internal void ResetIndicators() try { BatchBegin(); - Children.Clear(); AddExtraIndicatorItems(); } finally @@ -76,7 +83,6 @@ internal void ResetIndicatorCount(int oldCount) if (oldCount > _indicatorView.Count) { - RemoveRedundantIndicatorItems(); return; } @@ -101,6 +107,10 @@ void ResetIndicatorStylesNonBatch() var selectedIndex = position >= maxVisible ? maxVisible - 1 : position; bool isSelected = index == selectedIndex; var visualElement = Children[index] as VisualElement; + if (visualElement is null) + { + return; + } visualElement.BackgroundColor = isSelected ? GetColorOrDefault(_indicatorView.SelectedIndicatorColor, Colors.Gray) @@ -116,46 +126,29 @@ void ResetIndicatorStylesNonBatch() IsVisible = indicatorCount > 1 || !_indicatorView.HideSingle; } - Color GetColorOrDefault(Color color, Color defaultColor) => color ?? defaultColor; + Color GetColorOrDefault(Color? color, Color defaultColor) => color ?? defaultColor; void AddExtraIndicatorItems() { - var indicatorCount = _indicatorView.Count; - var indicatorMaximumVisible = _indicatorView.MaximumVisible; - var indicatorSize = _indicatorView.IndicatorSize; - var indicatorTemplate = _indicatorView.IndicatorTemplate; - - var oldCount = Children.Count; - var items = _indicatorView.ItemsSource.Cast().ToArray(); - for (var i = 0; i < indicatorCount - oldCount && i < indicatorMaximumVisible - oldCount; i++) + var indicatorSize = _indicatorView.IndicatorSize > 0 ? _indicatorView.IndicatorSize : 10; + var indicatorTemplate = _indicatorView.IndicatorTemplate ??= new DataTemplate(() => new Border { - var size = indicatorSize > 0 ? indicatorSize : 10; - var indicator = indicatorTemplate?.CreateContent() as View ?? new Frame + Padding = 0, + VerticalOptions = LayoutOptions.Center, + HorizontalOptions = LayoutOptions.Center, + WidthRequest = indicatorSize, + HeightRequest = indicatorSize, + StrokeShape = new RoundRectangle() { - Padding = 0, - HasShadow = false, - BorderColor = Colors.Transparent, - VerticalOptions = LayoutOptions.Center, - HorizontalOptions = LayoutOptions.Center, - WidthRequest = size, - HeightRequest = size, - CornerRadius = _indicatorView.IndicatorsShape == IndicatorShape.Circle ? (float)size / 2 : 0 - }; - indicator.BindingContext = items[i]; - var tapGestureRecognizer = new TapGestureRecognizer(); - tapGestureRecognizer.Tapped += (sender, args) => _indicatorView.Position = Children.IndexOf(sender as View); - indicator.GestureRecognizers.Add(tapGestureRecognizer); - Children.Add(indicator); - } - } - - void RemoveRedundantIndicatorItems() - { - var indicatorCount = _indicatorView.Count; - while (Children.Count > indicatorCount) - { - Children.RemoveAt(0); - } + CornerRadius = _indicatorView.IndicatorsShape == IndicatorShape.Circle + ? (float)indicatorSize / 2 + : 0, + Stroke = Colors.Transparent + } + }); + + BindableLayout.SetItemsSource(this, _indicatorView.ItemsSource); + BindableLayout.SetItemTemplate(this, indicatorTemplate); } public void Remove() From cc1905e20a3ab7a8caffb687ff7fe5c6ea8a0e26 Mon Sep 17 00:00:00 2001 From: Vladislav Antonyuk Date: Fri, 16 Feb 2024 00:38:43 +0200 Subject: [PATCH 3/6] Rename method --- src/Controls/src/Core/IndicatorView/IndicatorStackLayout.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Controls/src/Core/IndicatorView/IndicatorStackLayout.cs b/src/Controls/src/Core/IndicatorView/IndicatorStackLayout.cs index 3c90aa45623e..5070a2b241f1 100644 --- a/src/Controls/src/Core/IndicatorView/IndicatorStackLayout.cs +++ b/src/Controls/src/Core/IndicatorView/IndicatorStackLayout.cs @@ -62,7 +62,7 @@ internal void ResetIndicators() try { BatchBegin(); - AddExtraIndicatorItems(); + BindIndicatorItems(); } finally { @@ -86,7 +86,7 @@ internal void ResetIndicatorCount(int oldCount) return; } - AddExtraIndicatorItems(); + BindIndicatorItems(); } finally { @@ -128,7 +128,7 @@ void ResetIndicatorStylesNonBatch() Color GetColorOrDefault(Color? color, Color defaultColor) => color ?? defaultColor; - void AddExtraIndicatorItems() + void BindIndicatorItems() { var indicatorSize = _indicatorView.IndicatorSize > 0 ? _indicatorView.IndicatorSize : 10; var indicatorTemplate = _indicatorView.IndicatorTemplate ??= new DataTemplate(() => new Border From f502487846fb4724cd5b517e1e29c07c679a441b Mon Sep 17 00:00:00 2001 From: Vladislav Antonyuk Date: Fri, 16 Feb 2024 19:53:54 +0200 Subject: [PATCH 4/6] Add tests, replaced event with command --- .../IndicatorView/IndicatorStackLayout.cs | 7 ++- .../IndicatorViewLayoutTests.cs | 54 +++++++++++++++++++ 2 files changed, 59 insertions(+), 2 deletions(-) create mode 100644 src/Controls/tests/Core.UnitTests/IndicatorViewLayoutTests.cs diff --git a/src/Controls/src/Core/IndicatorView/IndicatorStackLayout.cs b/src/Controls/src/Core/IndicatorView/IndicatorStackLayout.cs index 5070a2b241f1..21a5865aaca3 100644 --- a/src/Controls/src/Core/IndicatorView/IndicatorStackLayout.cs +++ b/src/Controls/src/Core/IndicatorView/IndicatorStackLayout.cs @@ -20,8 +20,11 @@ protected override void OnChildAdded(Element child) if (child is View view) { - var tapGestureRecognizer = new TapGestureRecognizer(); - tapGestureRecognizer.Tapped += (sender, _) => _indicatorView.Position = Children.IndexOf(sender as View); + var tapGestureRecognizer = new TapGestureRecognizer + { + Command = new Command(sender => _indicatorView.Position = Children.IndexOf(sender)), + CommandParameter = view + }; view.GestureRecognizers.Add(tapGestureRecognizer); } } diff --git a/src/Controls/tests/Core.UnitTests/IndicatorViewLayoutTests.cs b/src/Controls/tests/Core.UnitTests/IndicatorViewLayoutTests.cs new file mode 100644 index 000000000000..e75d2b114be4 --- /dev/null +++ b/src/Controls/tests/Core.UnitTests/IndicatorViewLayoutTests.cs @@ -0,0 +1,54 @@ +using System.Collections.Generic; +using Xunit; + +namespace Microsoft.Maui.Controls.Core.UnitTests +{ + public class IndicatorViewTests : BaseTestFixture + { + [Fact] + public void IndicatorStackLayoutNoItems_ResetIndicators_ShouldHaveNoChildren() + { + // Arrange + var indicatorView = new IndicatorView(); + var indicatorStackLayout = new IndicatorStackLayout(indicatorView); + + // Act + indicatorStackLayout.ResetIndicators(); + + // Assert + Assert.Empty(indicatorStackLayout.Children); + } + + [Fact] + public void IndicatorStackLayoutWithItems_ResetIndicators_ShouldBindChildren() + { + // Arrange + var indicatorView = new IndicatorView() { ItemsSource = new List{"item1", "item2"} }; + var indicatorStackLayout = new IndicatorStackLayout(indicatorView); + + // Act + indicatorStackLayout.ResetIndicators(); + + // Assert + Assert.Equal(2, indicatorStackLayout.Children.Count); + } + + [Theory] + [InlineData(1, 2)] + [InlineData(0, 2)] + [InlineData(-2, 2)] + public void IndicatorStackLayout_ResetIndicatorCount_ShouldBindChildren(int oldCount, int expected) + { + // Arrange + var indicatorView = new IndicatorView() { ItemsSource = new List{"item1", "item2"} }; + var indicatorStackLayout = new IndicatorStackLayout(indicatorView); + Assert.Empty(indicatorStackLayout.Children); + + // Act + indicatorStackLayout.ResetIndicatorCount(oldCount); + + // Assert + Assert.Equal(expected, indicatorStackLayout.Children.Count); + } + } +} From 1847b2c685de4f22537233e879ce7eba8220e8a6 Mon Sep 17 00:00:00 2001 From: Rui Marinho Date: Mon, 18 Mar 2024 18:04:06 +0000 Subject: [PATCH 5/6] Update IndicatorStackLayout.cs --- src/Controls/src/Core/IndicatorView/IndicatorStackLayout.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Controls/src/Core/IndicatorView/IndicatorStackLayout.cs b/src/Controls/src/Core/IndicatorView/IndicatorStackLayout.cs index 21a5865aaca3..51768fd312b5 100644 --- a/src/Controls/src/Core/IndicatorView/IndicatorStackLayout.cs +++ b/src/Controls/src/Core/IndicatorView/IndicatorStackLayout.cs @@ -11,7 +11,7 @@ public IndicatorStackLayout(IndicatorView indicatorView) { _indicatorView = indicatorView; Orientation = StackOrientation.Horizontal; - _indicatorView.PropertyChanged += _indicatorViewPropertyChanged; + _indicatorView.PropertyChanged += IndicatorViewPropertyChanged; } protected override void OnChildAdded(Element child) @@ -29,7 +29,7 @@ protected override void OnChildAdded(Element child) } } - void _indicatorViewPropertyChanged(object? sender, PropertyChangedEventArgs e) + void IndicatorViewPropertyChanged(object? sender, PropertyChangedEventArgs e) { if (e.PropertyName == IndicatorView.IndicatorsShapeProperty.PropertyName || e.PropertyName == IndicatorView.IndicatorTemplateProperty.PropertyName) @@ -159,4 +159,4 @@ public void Remove() _indicatorView.PropertyChanged -= _indicatorViewPropertyChanged; } } -} \ No newline at end of file +} From 05aaf1d068bfe6c948de65a2ef4a16624c14212a Mon Sep 17 00:00:00 2001 From: Vladislav Antonyuk Date: Thu, 4 Apr 2024 18:06:00 +0300 Subject: [PATCH 6/6] Fix compilation issue --- src/Controls/src/Core/IndicatorView/IndicatorStackLayout.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Controls/src/Core/IndicatorView/IndicatorStackLayout.cs b/src/Controls/src/Core/IndicatorView/IndicatorStackLayout.cs index 51768fd312b5..1268984f95ae 100644 --- a/src/Controls/src/Core/IndicatorView/IndicatorStackLayout.cs +++ b/src/Controls/src/Core/IndicatorView/IndicatorStackLayout.cs @@ -156,7 +156,7 @@ void BindIndicatorItems() public void Remove() { - _indicatorView.PropertyChanged -= _indicatorViewPropertyChanged; + _indicatorView.PropertyChanged -= IndicatorViewPropertyChanged; } } }