Skip to content

Commit 684044a

Browse files
IndicatorView: Fix MaximumVisible not respected when using custom IndicatorTemplate (#31469)
<!-- Please let the below note in for people that find this PR --> > [!NOTE] > Are you waiting for the changes in this PR to be merged? > It would be very helpful if you could [test the resulting artifacts](https://github.com/dotnet/maui/wiki/Testing-PR-Builds) from this PR and let us know in a comment if this change resolves your issue. Thank you! ### Issue Details - When using IndicatorView with a custom IndicatorTemplate, the MaximumVisible property is not respected. ### Root Cause - When IndicatorView uses a custom IndicatorTemplate, the BindableLayout path is used to render indicators. In IndicatorStackLayout.BindIndicatorItems(), the full ItemsSource was passed directly to BindableLayout.SetItemsSource() without applying the MaximumVisible limit. - The MaximumVisible cap was only enforced in the native (non-template) code path. ### Description of Change - Modified BindIndicatorItems() in IndicatorStackLayout to call a new GetFilteredItemsSource() helper before binding. The helper returns a filtered List<object> capped at MaximumVisible items when the source has more items than the limit; otherwise, it returns the original ItemsSource unchanged. - This fix applies on both initial render and when MaximumVisible changes dynamically, because IndicatorView.MaximumVisibleProperty.propertyChanged calls ResetIndicators() → BindIndicatorItems(). ### Key Technical Details **IndicatorStackLayout.GetFilteredItemsSource():** 1. Returns null if ItemsSource is null or MaximumVisible <= 0 2. Returns the original ItemsSource if totalCount <= MaximumVisible (no filtering needed) 3. Otherwise enumerates up to MaximumVisible items into a new list of objects and returns it. **Code path (with IndicatorTemplate):** BindIndicatorItems() → GetFilteredItemsSource() → BindableLayout.SetItemsSource(filtered) **Code path (without IndicatorTemplate):** Not affected — native rendering handles MaximumVisible separately. ### Issues Fixed Fixes #31145 ### Validated the behaviour in the following platforms - [x] Windows - [x] Android - [x] iOS - [x] Mac ### Output | Platform | Before Fix | After Fix | |----------|----------|----------| | Android | <video src="https://github.com/user-attachments/assets/b077642e-d634-42a3-8d61-8374ac599b43"> | <video src="https://github.com/user-attachments/assets/cd4b29b1-4087-41cc-a7f3-67396accfe6b"> | | iOS | <video src="https://github.com/user-attachments/assets/297cfde8-bffe-4fcb-a08b-a43362c944ff"> | <video src="https://github.com/user-attachments/assets/8062d14e-ba38-4015-83dd-ca01e218ebfd"> | | Windows | <video src="https://github.com/user-attachments/assets/69aad00d-1f51-47fc-b92f-a8b4fe2741ac"> | <video src="https://github.com/user-attachments/assets/73eacf53-7d6d-4e87-a606-558ba3482158"> | | Mac | <video src="https://github.com/user-attachments/assets/49720bbc-ce67-49d6-95a0-6a8018b5d4fa"> | <video src="https://github.com/user-attachments/assets/8e494e0c-c795-4b62-894a-ddfdc4e4c27b"> |
1 parent f53c886 commit 684044a

7 files changed

Lines changed: 119 additions & 1 deletion

File tree

src/Controls/src/Core/IndicatorView/IndicatorStackLayout.cs

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
1+
using System;
2+
using System.Collections;
3+
using System.Collections.Generic;
14
using System.ComponentModel;
5+
using System.Linq;
26
using Microsoft.Maui.Controls.Shapes;
37
using Microsoft.Maui.Graphics;
48

@@ -161,10 +165,44 @@ void BindIndicatorItems()
161165
}
162166
});
163167

164-
BindableLayout.SetItemsSource(this, _indicatorView.ItemsSource);
168+
// Get the filtered items source based on MaximumVisible
169+
var itemsSource = GetFilteredItemsSource();
170+
BindableLayout.SetItemsSource(this, itemsSource);
171+
165172
BindableLayout.SetItemTemplate(this, indicatorTemplate);
166173
}
167174

175+
IEnumerable? GetFilteredItemsSource()
176+
{
177+
if (_indicatorView.ItemsSource is null || _indicatorView.MaximumVisible <= 0)
178+
{
179+
return null;
180+
}
181+
182+
var itemsSource = _indicatorView.ItemsSource;
183+
int totalCount = itemsSource is ICollection col
184+
? col.Count
185+
: itemsSource.Cast<object>().Count();
186+
187+
if (totalCount <= _indicatorView.MaximumVisible)
188+
{
189+
return itemsSource;
190+
}
191+
192+
var filteredItems = new List<object>(_indicatorView.MaximumVisible);
193+
foreach (var item in itemsSource)
194+
{
195+
if (filteredItems.Count >= _indicatorView.MaximumVisible)
196+
{
197+
break;
198+
}
199+
200+
filteredItems.Add(item);
201+
}
202+
203+
return filteredItems;
204+
}
205+
168206
public void Remove()
169207
{
170208
_indicatorView.PropertyChanged -= IndicatorViewPropertyChanged;
52.8 KB
Loading
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
namespace Maui.Controls.Sample.Issues;
2+
3+
[Issue(IssueTracker.Github, 31145, "MaximumVisible Property Not Working with IndicatorTemplate in IndicatorView", PlatformAffected.All)]
4+
public class Issue31145 : ContentPage
5+
{
6+
public Issue31145()
7+
{
8+
Label descriptionLabel = new Label
9+
{
10+
Text = "The test passes if the IndicatorView with an IndicatorTemplate respects the MaximumVisible property; otherwise, it fails.",
11+
FontSize = 18,
12+
HorizontalOptions = LayoutOptions.Center,
13+
};
14+
15+
IndicatorView indicatorView = new IndicatorView
16+
{
17+
ItemsSource = new List<string> { "Item1", "Item2", "Item3", "Item4", "Item5" },
18+
IndicatorColor = Colors.Yellow,
19+
SelectedIndicatorColor = Colors.Gray,
20+
HorizontalOptions = LayoutOptions.Center
21+
};
22+
23+
indicatorView.IndicatorTemplate = new DataTemplate(() =>
24+
{
25+
return new BoxView
26+
{
27+
WidthRequest = 10,
28+
HeightRequest = 10,
29+
HorizontalOptions = LayoutOptions.Center,
30+
VerticalOptions = LayoutOptions.Center
31+
};
32+
});
33+
34+
Button updateMaximumVisibleBtn = new Button
35+
{
36+
AutomationId = "UpdateMaximumVisibleBtn",
37+
Text = "Set MaximumVisible Property to 2"
38+
};
39+
40+
updateMaximumVisibleBtn.Clicked += (s, e) =>
41+
{
42+
indicatorView.MaximumVisible = 2;
43+
};
44+
45+
Content = new StackLayout
46+
{
47+
Padding = 20,
48+
Spacing = 20,
49+
Children =
50+
{
51+
descriptionLabel,
52+
indicatorView,
53+
updateMaximumVisibleBtn
54+
}
55+
};
56+
}
57+
}
26 KB
Loading
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
using NUnit.Framework;
2+
using UITest.Appium;
3+
using UITest.Core;
4+
5+
namespace Microsoft.Maui.TestCases.Tests.Issues;
6+
7+
public class Issue31145 : _IssuesUITest
8+
{
9+
public Issue31145(TestDevice device) : base(device)
10+
{
11+
}
12+
13+
public override string Issue => "MaximumVisible Property Not Working with IndicatorTemplate in IndicatorView";
14+
15+
[Test]
16+
[Category(UITestCategories.IndicatorView)]
17+
public void VerifyIndicatorViewMaximumVisibleWithTemplate()
18+
{
19+
App.WaitForElement("UpdateMaximumVisibleBtn");
20+
App.Tap("UpdateMaximumVisibleBtn");
21+
VerifyScreenshot();
22+
}
23+
}
13 KB
Loading
62.9 KB
Loading

0 commit comments

Comments
 (0)