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..1268984f95ae 100644
--- a/src/Controls/src/Core/IndicatorView/IndicatorStackLayout.cs
+++ b/src/Controls/src/Core/IndicatorView/IndicatorStackLayout.cs
@@ -1,23 +1,35 @@
-#nullable disable
-using System;
-using System.Collections;
-using System.Collections.Specialized;
using System.ComponentModel;
+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;
Orientation = StackOrientation.Horizontal;
- _indicatorView.PropertyChanged += _indicatorViewPropertyChanged;
+ _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
+ {
+ Command = new Command(sender => _indicatorView.Position = Children.IndexOf(sender)),
+ CommandParameter = view
+ };
+ view.GestureRecognizers.Add(tapGestureRecognizer);
+ }
+ }
+
+ void IndicatorViewPropertyChanged(object? sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == IndicatorView.IndicatorsShapeProperty.PropertyName
|| e.PropertyName == IndicatorView.IndicatorTemplateProperty.PropertyName)
@@ -53,8 +65,7 @@ internal void ResetIndicators()
try
{
BatchBegin();
- Children.Clear();
- AddExtraIndicatorItems();
+ BindIndicatorItems();
}
finally
{
@@ -75,11 +86,10 @@ internal void ResetIndicatorCount(int oldCount)
if (oldCount > _indicatorView.Count)
{
- RemoveRedundantIndicatorItems();
return;
}
- AddExtraIndicatorItems();
+ BindIndicatorItems();
}
finally
{
@@ -100,6 +110,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)
@@ -115,49 +129,34 @@ 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()
+ void BindIndicatorItems()
{
- var indicatorCount = _indicatorView.Count;
- var indicatorMaximumVisible = _indicatorView.MaximumVisible;
- var indicatorSize = _indicatorView.IndicatorSize;
- var indicatorTemplate = _indicatorView.IndicatorTemplate;
-
- var oldCount = Children.Count;
- 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
- };
- 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()
{
- _indicatorView.PropertyChanged -= _indicatorViewPropertyChanged;
+ _indicatorView.PropertyChanged -= IndicatorViewPropertyChanged;
}
}
-}
\ No newline at end of file
+}
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);
+ }
+ }
+}