[Android] CV RemainingItemsThresholdReachedCommand fires on initial data load - fix#28321
[Android] CV RemainingItemsThresholdReachedCommand fires on initial data load - fix#28321kubaflo wants to merge 205 commits intodotnet:inflight/currentfrom
Conversation
|
/azp run |
|
Azure Pipelines successfully started running 3 pipeline(s). |
jsuarezruiz
left a comment
There was a problem hiding this comment.
Could you include a test based on https://github.com/DharunAR/MAUICollectionView?
Done |
🤖 AI Summary🚦 Test VerificationResult: ✅ PASSED Expand DetailsTest Verification ReportDate: 2026-01-28 15:19:07 | Platform: ANDROID | Status: ✅ PASSED Summary
✅ Final VerdictVERIFICATION PASSED ✅ The tests correctly detect the issue:
Conclusion: The tests properly validate the fix and catch the bug when it's present. ConfigurationPlatform: android Fix Files
Test Results DetailsTest Run 1: WITHOUT FixExpected: Tests should FAIL (bug is present) Test Run 2: WITH FixExpected: Tests should PASS (bug is fixed) Test: |
There was a problem hiding this comment.
Pull request overview
Fixes an Android CollectionView regression where RemainingItemsThresholdReachedCommand can fire during initial layout/data load instead of only after user-initiated scrolling.
Changes:
- Gate
RemainingItemsThresholdReacheddispatch on Android behind an actual scroll-state transition (non-idle). - Add a new HostApp issue page reproducing the scenario with delayed initial item load.
- Add an Appium UI test asserting the command does not fire on initial data load.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 4 comments.
| File | Description |
|---|---|
| src/Controls/src/Core/Handlers/Items/Android/RecyclerViewScrollListener.cs | Tracks scroll state and prevents threshold events firing before scrolling begins. |
| src/Controls/tests/TestCases.HostApp/Issues/Issue28321.xaml | Adds a repro UI for the delayed-load + threshold command scenario. |
| src/Controls/tests/TestCases.HostApp/Issues/Issue28321.xaml.cs | Adds the repro page code-behind and view model to load items and append on threshold command. |
| src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue28321.cs | Adds a UI test validating the initial item count remains unchanged (command not invoked). |
| [Issue(IssueTracker.Github, 28321, "CV RemainingItemsThresholdReachedCommand fires on initial data load", PlatformAffected.Android)] | ||
| public partial class Issue28321 : ContentPage | ||
| { | ||
| public readonly record struct Data(string Text, string AutomationId); |
There was a problem hiding this comment.
Data record struct is declared but never used. Please remove it to avoid dead code/noise in this new test page.
| public readonly record struct Data(string Text, string AutomationId); |
| x:Class="Maui.Controls.Sample.Issues.Issue28321" | ||
| Title="Issue28321"> |
There was a problem hiding this comment.
The XAML page is named/title’d Issue28321, but this PR is intended to fix issue #28279 per the PR title/description. Please rename the page/class to match the actual issue number so the UI test infrastructure navigates to the correct page.
| public class Issue28321 : _IssuesUITest | ||
| { | ||
| public Issue28321(TestDevice device) : base(device) { } | ||
|
|
||
| public override string Issue => "CV RemainingItemsThresholdReachedCommand fires on initial data load"; |
There was a problem hiding this comment.
This new UI test class/file is named Issue28321, but the PR/linked report is #28279. Please align the test name/issue number with the actual issue being fixed so the test clearly covers the reported regression (rename test class/file and corresponding HostApp page).
| [Issue(IssueTracker.Github, 28321, "CV RemainingItemsThresholdReachedCommand fires on initial data load", PlatformAffected.Android)] | ||
| public partial class Issue28321 : ContentPage |
There was a problem hiding this comment.
PR metadata says this change fixes GitHub issue #28279, but this new HostApp issue page is numbered 28321 (file/class name and [Issue] attribute). Please align the issue number with the actual fixed issue so the test page is discoverable and traceable (rename file/class and update the [Issue] attribute accordingly).
|
When should we expect to see this fixed? This is a very serious bug! |
|
🚀 Dogfood this PR with:
curl -fsSL https://raw.githubusercontent.com/dotnet/maui/main/eng/scripts/get-maui-pr.sh | bash -s -- 28321Or
iex "& { $(irm https://raw.githubusercontent.com/dotnet/maui/main/eng/scripts/get-maui-pr.ps1) } 28321" |
…ertPageBefore (dotnet#29924) <!-- 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 Detail When using the InsertPageBefore method to change the root page in a NavigationPage, the back button is not visible, but the flyout icon remains visible. ### Root Cause The flyout icon was not updated when changing the root page using InsertPageBefore. ### Description of Change The updated logic checks whether the inserted page becomes the new root, updates the flyout icon. Previously, the flyout icon was updated based on NavPageController.Pages. However, this collection was not updated with the actual native navigation stack. Since pages are inserted directly into ViewControllers in InsertPageBefore, the fix updates the flyout icon based on the ViewControllers stack instead. ### Tested the behavior in the following platforms - [x] Android - [x] Windows - [x] iOS - [x] Mac ### Issues Fixed Fixes dotnet#29921 ### Screenshots | Before Issue Fix | After Issue Fix | |----------|----------| | <video width="300" height="600" src="https://github.com/user-attachments/assets/20acfd44-79af-45c7-ad9d-c55f2e825b49"> | <video width="300" height="600" src="https://github.com/user-attachments/assets/fc99cc8b-8ed2-424f-876c-bd703e10872f">) |
…is set to null (dotnet#30420) <!-- 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! ### Root Cause CollectionView.SelectItem was being called inside the `PerformBatchUpdates` completion handler, which is triggered after all other actions are completed. As a result, when `SelectedItem` is set to null in the `SelectionChanged` event handler, the deferred selection inside `PerformBatchUpdates` would fire afterward and re-select the item, making the null assignment ineffective. The original implementation (from PR dotnet#25555) always wrapped `SelectItem` calls in `PerformBatchUpdates` to ensure selection happened after collection view items generation was completed. This worked for initial load scenarios but caused a timing issue for runtime selection changes. ### Description of Change The fix introduces conditional logic based on the view's loaded state using `CollectionView.IsLoaded()` (which checks if `UIView.Window != null`): **For initial load (!IsLoaded()):** - Selection still uses `PerformBatchUpdates` to defer until items are generated - This preserves the original intent from PR dotnet#25555 **For runtime changes (IsLoaded()):** - Selection executes immediately without `PerformBatchUpdates` wrapper - Includes all existing safety checks: EmptySource verification, reference equality, index recalculation, and item equality validation - Allows user code (like `SelectedItem = null`) to take effect immediately without being overridden by deferred selection This resolves the issue where the selected item was not being cleared when `SelectedItem` is set to null during runtime. ### Key Technical Details **IsLoaded() Extension Method:** - Definition: `UIView.Window != null` - Indicates whether the view is attached to the window hierarchy - Used to distinguish between initial load (preselection) vs. runtime selection changes **Lifecycle Distinction:** - **Initial load**: View isn't attached, items still being laid out → defer selection - **Runtime**: View is active, items stable → select immediately to avoid race conditions **Why Immediate Selection Works:** Direct `SelectItem` calls execute synchronously in the current call stack, before any completion handlers fire. This prevents the race condition where user code sets `SelectedItem = null`, but the deferred `PerformBatchUpdates` completion handler re-selects the item afterward. ### Files Changed 1. `src/Controls/src/Core/Handlers/Items/iOS/SelectableItemsViewController.cs` - Added IsLoaded() check (deprecated handler) 2. `src/Controls/src/Core/Handlers/Items2/iOS/SelectableItemsViewController2.cs` - Added IsLoaded() check (current handler) 3. `src/Controls/tests/TestCases.HostApp/Issues/Issue30363.cs` - New UI test demonstrating the fix 4. `src/Controls/tests/TestCases.HostApp/Issues/Issue26187.cs` - Updated existing test 5. `src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue30363.cs` - Appium test with screenshot verification 6. Snapshots for iOS and Android ### What NOT to Do (for future agents) - ❌ **Don't always use PerformBatchUpdates for selection** - This causes deferred execution that can override user code - ❌ **Don't remove PerformBatchUpdates entirely** - Initial load scenarios still need it for proper item generation timing - ❌ **Don't ignore the view's loaded state** - The lifecycle context (initial vs. runtime) is critical for correct timing ### Edge Cases | Scenario | Risk | Mitigation | |----------|------|------------| | EmptySource disposal | Medium | Runtime path checks `ItemsSource is EmptySource` before selection | | ItemsSource changes during selection | Medium | Runtime path verifies `ReferenceEquals(ItemsView.ItemsSource, originalSource)` | | Collection mutations (add/delete) | Medium | Runtime path recalculates index and verifies item equality at updated position | | Initial preselection timing | Low | Preserved PerformBatchUpdates for !IsLoaded() case | ### Issues Fixed Fixes dotnet#30363 Fixes dotnet#26187 ### Regression PR This fix addresses a regression introduced in PR dotnet#25555, which added `PerformBatchUpdates` to ensure selection timing but didn't account for runtime selection clearing scenarios. ### Platforms Tested - [x] iOS - [x] Mac - [x] Android - [x] Windows ### Screenshots **Issue dotnet#30363:** | Before Fix | After Fix | |----------|----------| | <video src="https://github.com/user-attachments/assets/09891481-5e3a-476d-a058-b6f828335a63"> | <video src="https://github.com/user-attachments/assets/6bad46a2-acbf-498a-a45c-e08c84f4a32a"> | **Issue dotnet#26187:** | Before Fix | After Fix | |----------|----------| | <video src="https://github.com/user-attachments/assets/95245bc1-5772-4cc1-9947-c371a4c35586"> | <video src="https://github.com/user-attachments/assets/1474b60e-d552-4a05-9461-fb513e3ef5b0"> |
…#33481) <!-- Are you targeting main? All PRs should target the main branch unless otherwise noted. --> ### Description of Change This pull request introduces support for a Material Design 3 styled `ActivityIndicator` on Android, allowing the app to use the new Material component when the Material3 feature is enabled. The main changes involve conditional registration of a new handler, the implementation of a `MaterialActivityIndicatorHandler`, and the addition of a custom `MaterialActivityIndicator` control for Android. **Material3 ActivityIndicator support for Android:** * Added a new `MaterialActivityIndicatorHandler` class that extends `ActivityIndicatorHandler` and creates a `MaterialActivityIndicator` (the new Material3 control) as its platform view. It also customizes layout behavior to ensure proper sizing and centering. * Implemented the `MaterialActivityIndicator` control in `MaterialActivityIndicator.cs`, inheriting from `CircularProgressIndicator` and overriding measurement logic to ensure the indicator remains square and properly sized according to Material guidelines. **Handler registration logic:** * Updated the `AddControlsHandlers` extension method to conditionally register either `MaterialActivityIndicatorHandler` or the classic `ActivityIndicatorHandler` for `ActivityIndicator`, based on whether Material3 is enabled on Android. ### Issues Fixed <!-- Please make sure that there is a bug logged for the issue being fixed. The bug should describe the problem and how to reproduce it. --> Fixes dotnet#33479 **Material3 Spec** [ActivityIndicator](https://m3.material.io/components/progress-indicators/specs) ### Output Screenshot | Material 2 | Material 3 | |---------|--------| | <video height=600 width=300 src="https://github.com/user-attachments/assets/23d67157-b19f-4b5d-b21d-4e50c45f288f"> | <video height=600 width=300 src="https://github.com/user-attachments/assets/6a5de984-6407-4a5c-b60b-09e9fc16342a"> |
…dotnet#33246) <!-- 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! ## Background and Context This PR addresses issue dotnet#33037 where iOS large titles in Shell don't transition properly when scrolling. This is the third attempt to fix the issue: 1. **PR dotnet#33039** (merged Dec 8, 2025): Changed the condition from `PrefersLargeTitles = largeTitleDisplayMode == Always` to `PrefersLargeTitles = largeTitleDisplayMode != Never` 2. **PR dotnet#33230** (merged Dec 19, 2025): Reverted dotnet#33039 because it caused "a lot of failing tests" - large titles started appearing unintentionally in many scenarios, breaking UI test snapshots 3. **PR dotnet#33246** (this PR): Re-attempts the fix with an important addition: opt-in check ## Root Cause Shell doesn't have a `PrefersLargeTitles` property at the Shell level. The previous implementation would update large title preferences whenever any page was displayed, regardless of whether the developer explicitly requested large title behavior. **Problem with PR dotnet#33039:** Changing the condition to `largeTitleDisplayMode != Never` meant that: - `LargeTitleDisplayMode.Always` → PrefersLargeTitles = true ✅ - `LargeTitleDisplayMode.Automatic` → PrefersLargeTitles = true⚠️ (unintended for pages that didn't set the property) - `LargeTitleDisplayMode.Never` → PrefersLargeTitles = false ✅ Since the default value is `Automatic`, pages that never set `LargeTitleDisplay` would suddenly get large titles, breaking many UI tests. ## Description of Change This PR implements an **opt-in model** for large title display in Shell: ### 1. Added Opt-In Check Before updating large title preferences, the code now checks if the `LargeTitleDisplay` property is explicitly set on the Page: ```csharp if (!page.IsSet(PlatformConfiguration.iOSSpecific.Page.LargeTitleDisplayProperty)) { return; // Don't update if property not explicitly set }
…rItems (dotnet#34085) <!-- 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 - On iOS 26, setting Shell.ForegroundColor (either at the Shell level or Page level) no longer applies the specified color to ToolbarItems. - The toolbar items remain in the default system tint color instead of respecting the configured foreground color. ### Root Cause - On iOS 26, Apple's LiquidGlass redesign changed how UINavigationBar.TintColor propagates to bar button items — it is no longer automatically inherited. ### Description of Change - Added UpdateRightBarButtonItemTintColors() method in ShellPageRendererTracker.cs to explicitly set the TintColor of right bar button items to the Shell's foreground color for iOS 26+ and Mac Catalyst 26+. This ensures toolbar items correctly reflect the intended color. - Updated property change handlers (HandleShellPropertyChanged, OnPagePropertyChanged) to call UpdateRightBarButtonItemTintColors() when relevant properties change, guaranteeing color updates are applied when needed. ### Issues Fixed Fixes dotnet#34083 ### Validated the behaviour in the following platforms - [ ] Windows - [ ] Android - [x] iOS - [x] Mac ### Output | Before | After | |----------|----------| | <img src="https://github.com/user-attachments/assets/dc70555a-7e03-4922-94d5-1f6723c059f1"> | <img src="https://github.com/user-attachments/assets/30a18bca-103d-4d76-a4ef-956fbbef3fe9"> |
…ling Permissions.CheckStatusAsync<Permissions.Microphone>() (dotnet#33179) <!-- !!!!!!! MAIN IS THE ONLY ACTIVE BRANCH. MAKE SURE THIS PR IS TARGETING MAIN. !!!!!!! --> ### Issue Details On .NET 10 Windows, calling Permissions.CheckStatusAsync<Permissions.Microphone>() throws an exception when the app is running as an unpackaged app. This is a regression introduced after Windows apps were changed to run as unpackaged by default. In unpackaged apps, AppxManifest.xml is not used, so microphone capabilities declared in the manifest are ignored. However, the current implementation always validates microphone capability declarations against AppxManifest.xml, which causes an exception for unpackaged apps. ### Description of Change <!-- Enter description of the fix in this section --> Updated the Windows microphone permission logic to skip manifest capability checks for unpackaged apps. The microphone declaration is now required only for packaged apps, preventing exceptions and aligning the behavior with other Essentials APIs. ### Issues Fixed <!-- Please make sure that there is a bug logged for the issue being fixed. The bug should describe the problem and how to reproduce it. --> Fixes dotnet#32989 <!-- Are you targeting main? All PRs should target the main branch unless otherwise noted. --> **Tested the behavior in the following platforms.** - [ ] Android - [x] Windows - [ ] iOS - [ ] Mac | Before | After | |---------|--------| | **Windows**<br> <img src="https://github.com/user-attachments/assets/7870d1a7-eeab-4e85-be4d-b951f0bd3d19" width="600" height="300"> | **Windows**<br> <img src="https://github.com/user-attachments/assets/bd43f5b8-85b7-4e3c-8404-d01c165fefa4" width="600" height="300"> | ---------
…ackground (dotnet#29225) ### Issue details Setting SearchBar background to transparent was showing black color on iOS. ### Description of changes Handled transparent background in SearchBar by setting an empty background image and UIColor.Clear. If the background is not transparent, reset the background image to null to allow applying backgrounds properly. <!-- !!!!!!! MAIN IS THE ONLY ACTIVE BRANCH. MAKE SURE THIS PR IS TARGETING MAIN. !!!!!!! --> <!-- Enter description of the fix in this section --> ### Issues Fixed <!-- Please make sure that there is a bug logged for the issue being fixed. The bug should describe the problem and how to reproduce it. --> Fixes dotnet#11677 **Tested the behavior in the following platforms.** - [x] Android - [x] Windows - [x] iOS - [x] Mac ### Output Screenshot | Before | After | |---------|--------| | **iOS**<br> <video src="https://github.com/user-attachments/assets/80fdaaa1-8ddf-4315-bffa-cf3463a88a98" width="300" height="600"> | **iOS**<br> <video src="https://github.com/user-attachments/assets/51ba2b0e-2a05-4e8a-8e75-0c448ad253c8" width="300" height="600"> |
…t Applied in Horizontal Layouts (dotnet#29474) <!-- 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 - [Android] CollectionView MeasureFirstItem ItemSizingStrategy Not Applied in Horizontal Layouts. ### Root Cause - The first item's size was not applied correctly to subsequent items because the _pixelSize field was being set to null when using a horizontal orientation. ### Description of Changes - Removed the assignment of null to the _pixelSize field, ensuring the first item's size is preserved and used for measuring subsequent items. - Introduced the UpdateAdapter method to re-measure and apply the first item’s size to all items when switching the layout orientation from vertical to horizontal. Validated the behaviour in the following platforms - [x] Android - [x] Windows , iOS, MacOS - CollectionView MeasureFirstItem sizing not applied on Windows, iOS and macOS [dotnet#29130] ### Issues Fixed Fixes dotnet#29192 ### Output images Android | Before | After | |---------|--------| |<video src="https://github.com/user-attachments/assets/bbafe55b-a665-4e4f-8f19-a722f3db29cf" > | <img src="https://github.com/user-attachments/assets/42eade41-ebce-4490-8666-6a51364988d6" width="300" /> |
…, works on Windows (dotnet#27847) ### Issue Details: ItemsView not visible when Grouping is enabled and ItemSizingStrategy property set to MeasureFirstItem for CollectionView in android platform. ### Root Cause: The ItemSizingStrategy property is set to MeasureFirstItem for the CollectionView in the sample. As a result, the CollectionView calculates the size of the first item (which is a group header template) and applies this size to all subsequent templated items during binding. When the second item (defined by the ItemTemplate) is measured, it uses the size of the first item. However, during the layout phase, the second item respects the height request specified in the DataTemplate and is laid out according to that height. This mismatch between the measured size and the layout size for the ItemTemplate results in incorrect dimensions, causing the item to not display properly in the view. ### Description of Change: I calculated the size from the ItemTemplate instead of the group header template or any other template by adding a condition to check if templatedItemViewHolder.ItemViewType == ItemViewType.TemplatedItem. This allows the calculated size from the first ItemTemplate to be reused for all subsequent ItemTemplate items. As a result, the correct dimensions are measured and laid out according to the calculated size. **Tested the behavior in the following platforms.** - [x] Android - [x] Windows - [x] iOS - [x] Mac ### Reference: N/A ### Issues Fixed: Fixes dotnet#20855 Fixes dotnet#29191 Fixes dotnet#32578 ### Screenshots | Before | After | |---------|--------| | <img width="259" alt="Screenshot 2025-02-17 at 7 58 19 PM" src="https://github.com/user-attachments/assets/f3ff0dba-39cd-4d19-aac4-2b9db1e82995" /> | <img width="259" alt="Screenshot 2025-02-17 at 7 52 33 PM" src="https://github.com/user-attachments/assets/1a1e446d-be9f-44ec-8742-774d969ab7c8" /> |
…is null (dotnet#32314) <!-- 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 - On Windows, TimePicker renders a default time when its value is null, both on first load and after runtime updates, instead of showing an empty state. - On macOS, DatePicker and TimePicker render the default date/time when their values are null, again both initially and dynamically, rather than remaining empty. ### Root Cause Windows : - The TimePicker handler uses the null-coalescing operator with TimeSpan.Zero when the Time property is null. As a result, it defaults to 12:00 AM in 12-hour format and 00:00 in 24-hour format. ### Description of Change - Updated TimePickerExtensions.UpdateTime to clear the native control's time value when the logical time is null, instead of defaulting to midnight. - Documentation : [Time Picker](https://learn.microsoft.com/en-us/windows/apps/design/controls/time-picker#:~:text=When%20SelectedTime%20is%20null%2C%20the%20picker%20is%20%27unset%27%20and%20shows%20the%20field%20names%20instead%20of%20a%20time.) **Regarding Mac Platform :** - The same behavior is observed on the native platform as well. The values are not cleared when the date or time picker is set to null. In such cases, the default date or time is rendered. ### Issues Fixed Fixes dotnet#32266 ### Validated the behaviour in the following platforms - [x] Windows - [x] Android - [x] iOS - [x] Mac ### Output | Before | After | |----------|----------| | <img src="https://github.com/user-attachments/assets/70d2fda1-66e4-4083-8df5-75c064ada067"> | <img src="https://github.com/user-attachments/assets/33f4fc5f-456f-4de8-b72c-99c07dc07eaa"> |
<!-- 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! <!-- !!!!!!! MAIN IS THE ONLY ACTIVE BRANCH. MAKE SURE THIS PR IS TARGETING MAIN. !!!!!!! --> ### Root Cause of the issue - The native WinUI ToggleSwitch control defines a default [MinWidth](https://github.com/microsoft/microsoft-ui-xaml/blob/a3021d80977fecc7d6ca33dc94dc500c5535faab/src/controls/dev/CommonStyles/ToggleSwitch_themeresources.xaml#L188C20-L188C45) of 154 in its style, likely to accommodate the OnContent and OffContent values supported in WinUI. However, since .NET MAUI does not support displaying these contents, the default minimum width is unnecessary. As a result, the control always reserves that space, leading to unnecessary space usage in .NET MAUI. ### Description of Change - The native ToggleSwitch's minimum width is now set based on the MinimumWidthRequest value provided by the MAUI Switch view. Since MAUI does not support OnContent and OffContent, the default width is unnecessary. This change ensures the control's width reflects the actual layout requirement, resolving the unwanted spacing issue. ### Reference - [ToggleSwitchThemeMinWidth](https://github.com/microsoft/microsoft-ui-xaml/blob/a3021d80977fecc7d6ca33dc94dc500c5535faab/src/controls/dev/CommonStyles/ToggleSwitch_themeresources.xaml#L188C20-L188C45) - [DefaultSpacing](https://github.com/microsoft/microsoft-ui-xaml/blob/a3021d80977fecc7d6ca33dc94dc500c5535faab/src/controls/dev/CommonStyles/ToggleSwitch_themeresources.xaml#L501) ### Issues Fixed Fixes dotnet#28901 Fixes dotnet#30273 Fixes dotnet#10107 ### Tested the behaviour in the following platforms - [x] iOS - [x] Mac - [x] Android - [x] Windows ### Screenshot | Before Fix | After Fix | |----------|----------| | <video src="https://github.com/user-attachments/assets/d8f0c4da-1199-457f-8186-4bf80495c9c8"> | <video src="https://github.com/user-attachments/assets/8128cfbf-1aaf-46c9-a059-45e512114f73"> |
…umTrackColor (dotnet#30089) <!-- 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! ### Root Cause of the issue - On Windows, in the default slider style, the [SliderTrackFill](https://github.com/microsoft/microsoft-ui-xaml/blob/main/src/controls/dev/CommonStyles/Slider_themeresources.xaml) resource is mapped to the slider’s background and is used to render the maximum (unfilled) portion of the track. In the normal visual state, the slider background remains unchanged and uses the default style. When a background color is applied via the Slider.Background property, it occupies the same visual area used to render the maximum track. - As a result, when both MaximumTrackColor and Background are set, the background color takes precedence in the normal state, effectively hiding the MaximumTrackColor. Since no visual state override exists for the background in the default template, the assigned background remains active and visually overrides the maximum track. - However, during interaction states like PointerOver or Pressed, WinUI applies the MaximumTrackColor to the HorizontalTrackRect, making it appear only on hover or press. - The core issue is that the background color was applied using the Slider.Background property instead of updating the appropriate SliderContainerBackground resource. On Windows, this resource controls the background of the slider’s container and is the correct way to apply background without interfering with maximum track rendering. ### Description of Change - Implemented a mapper for the Slider background color on Windows that updates the SliderContainerBackground and related theme resources. This ensures the background is applied to the slider's root layout, consistent with other platforms. As a result, both Background and MaximumTrackColor now render correctly without overlapping, resolving the issue. This approach ensures visual consistency with how the slider appears on other platforms. ### Issues Fixed Fixes dotnet#25921 ### Tested the behaviour in the following platforms - [x] Windows - [x] iOS - [x] Mac - [x] Android ### Screenshot **Note:** In this video, the `BackgroundColor` is set to `yellow`, and the `MaximumTrackColor` is set to `red`. | Before Fix | After Fix | |----------|----------| | <video src="https://github.com/user-attachments/assets/815d6e24-94f4-42ec-8e68-2abbcd0f0ba5"> | <video src="https://github.com/user-attachments/assets/9f4d5847-5db1-4b8c-9dc9-1efffa95fc27"> |
### Issue Detail When navigating to a page using Shell with query parameters (e.g., via [QueryProperty]), the expected query value was not available during navigation. ### Root Cause In Shell navigation with query parameters, the query attributes were being set on the page after it was assigned to the Content property. As a result, the query value was not available when the navigation occurred. ### Description of Change The query parameters (QueryAttributesProperty) are now assigned to the page before it is set to ContentCache in ShellContent. This ensures the query value is available to the page before the Navigated event is triggered. ### Tested the behavior in the following platforms - [x] Android - [x] Windows - [x] iOS - [x] Mac ### Issues Fixed Fixes dotnet#10509 Fixes dotnet#11113 Fixes dotnet#29645 Fixes dotnet#24241 ### Screenshots | Before Issue Fix | After Issue Fix | |----------|----------| | <video width="300" height="600" src="https://github.com/user-attachments/assets/23e15d65-0f11-431f-9c2e-6c7fb3a30364"> | <video width="300" height="600" src="https://github.com/user-attachments/assets/e566f8e5-a029-45ea-9172-5369130ee6ab">) |
### RootCause: When updating the spacing value, the VerticalItem ContainerStyle is applied, which results in the spacing value being updated to vertical, even when using HorizontalList ### Description of Change: I modified it by checking the orientation and apply the appropriate ItemContainerStyle. Now, it adjusts the spacing accordingly. ### Tested the behaviour in the following platforms - [x] Windows - [x] Android - [x] iOS - [x] Mac ### Issue Fixed: Fixes: dotnet#23377 ### Output | Before Issue Fix | After Issue Fix | |----------|----------| | <video src="https://github.com/user-attachments/assets/ec537d52-e07c-4c64-8ee4-682cb9678ede"> | <video src="https://github.com/user-attachments/assets/0205fa8b-f3b6-466e-8fb4-84c8765ca6d7"> |
…n the shell search handler (dotnet#28474) ### Issue Details * When the search controller has an empty item source and is focused on the Shell search handler, the flyout icon and content page title disappear with no option to make them visible again. ### Root Cause * The ShowsCancelButton property of the Shell's UISearchController search bar is set to false. As a result, when the search controller is focused, the cancel button does not appear, preventing the flyout icon and content page title from being visible when the search controller has an empty item source. ### Description of Changes * Setting ShowsCancelButton to false is no longer necessary, as this behavior is now handled by the automaticallyShowsCancelButton property in UISearchBar. * The cancel button ensures that the flyout icon and content page title return to their normal visibility after focusing and unfocusing. When clicking the cancel button, the flyout icon and content page title are now correctly displayed, when the search controller has an empty item source. ### Issues Fixed Fixes dotnet#22060 Validated the behaviour in the following platforms - [x] Android - [x] Windows - [x] iOS - [x] Mac ### Output |Before|After| |--|--| | <video src="https://github.com/user-attachments/assets/6df06656-bc5e-4627-b940-f6b6ca91e994" >| <video src="https://github.com/user-attachments/assets/c3120cd8-b258-4858-af09-13ea65574f62">|
<!-- !!!!!!! MAIN IS THE ONLY ACTIVE BRANCH. MAKE SURE THIS PR IS TARGETING MAIN. !!!!!!! --> ### Description of Change When calculating the width and height of the cell, add space to prevent using the wrong view size to calculate the platform size. **iOS**: | Before fix | After fix | |--------|--------| | <img src="https://github.com/user-attachments/assets/93dc4912-0fce-49c1-bc06-05ef03d3f711" width="300" /> | <img src="https://github.com/user-attachments/assets/ae1f5a40-f440-437b-8353-58b3d02cbdea" width="300" /> | **Android**: | Before fix | After fix | |--------|--------| | <img src="https://github.com/user-attachments/assets/5b673c29-6880-4e27-9bdb-d3119df869b7" width="300" /> | <img src="https://github.com/user-attachments/assets/920e3c0b-0861-4e66-b98f-140c9f850a09" width="300" /> | **maccatalyst**: | Before fix | After fix | |--------|--------| | <img src="https://github.com/user-attachments/assets/312a5408-645d-46ba-abc3-fa6cbbccf14c" width="500" /> | <img src="https://github.com/user-attachments/assets/ceba112a-20fb-40c7-a37a-b4fa0e208083" width="500" /> | <!-- Enter description of the fix in this section --> ### Issues Fixed <!-- Please make sure that there is a bug logged for the issue being fixed. The bug should describe the problem and how to reproduce it. --> Fixes dotnet#26633 <!-- Are you targeting main? All PRs should target the main branch unless otherwise noted. --> ---------
…idth is set (dotnet#25748) ### Root cause - When setting the width request explicitly on a vertical layout, the layout content of the label does not update its width and height constraints correctly. When setting the width request in label inside the vertical layout, the layout content of the label does not update its width and height constraints correctly. It causes the label text cropped. - This is due to a missing explicit calculation for the label and layout in the GetDesiredSizeFromHandler(this IViewHandler viewHandler, double widthConstraint, double heightConstraint) method in the ViewHandlerExtension.iOS class. ## Description of changes - Included the IsExplicitSet calculation for layout and label to update the width and height constraints. ### Issues Fixed Fixes dotnet#15559 ### Tested the behavior in the following platforms - [x] Android - [x] Windows - [x] iOS - [x] Mac ### ScreenShot iOS | Before | After | |---------|--------| |<img src="https://github.com/user-attachments/assets/55914247-95b7-4a3e-a827-1e4bab040b11" width="300" height="600"> | <img src="https://github.com/user-attachments/assets/bdcff9a9-fb53-449f-9853-20c93c075def" width="300" height="600"> | macOS | Before | After | |---------|--------| |<img src="https://github.com/user-attachments/assets/e7d13898-6eae-42f0-98a7-21b6a51a71d3"> | <img src="https://github.com/user-attachments/assets/8502c8ec-a765-4998-8886-57930065c9f2" > |
…#34535) > [!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! <!-- !!!!!!! MAIN IS THE ONLY ACTIVE BRANCH. MAKE SURE THIS PR IS TARGETING MAIN. !!!!!!! --> ### Issue Details Flex-grow calculation was distributing the entire container space instead of just the free space, causing items to be sized incorrectly. ### Description of Change <!-- Enter description of the fix in this section --> * Calculate actual free space: freeSpace = flex_dim - extra_flex_dim * Distribute only free space proportionally based on grow ratios * Preserve item's intrinsic size instead of resetting to 0 ### Issues Fixed <!-- Please make sure that there is a bug logged for the issue being fixed. The bug should describe the problem and how to reproduce it. --> Fixes dotnet#34464 <!-- Are you targeting main? All PRs should target the main branch unless otherwise noted. --> **Tested the behavior in the following platforms.** - [x] Android - [x] Windows - [x] iOS - [x] Mac | Before | After | |---------|--------| | **Mac**<br> <img src="https://github.com/user-attachments/assets/2f155ddd-a04a-4217-9bd8-f294357ba171" width="600" height="300"> | **Mac**<br> <img src="https://github.com/user-attachments/assets/cb840cc4-7c7b-4a30-aeb6-8db3a8458c79" width="600" height="300"> | --------- Co-authored-by: Jakub Florkowski <42434498+kubaflo@users.noreply.github.com>
…control edge (dotnet#34362) Fixes dotnet#34119 When dragging from the edge of a control on Windows, `PointerExited` can fire before the manipulation begins. In this scenario the pointer ID is removed from the `_fingers` collection before `HandlePan` runs, resulting in `_fingers.Count == 0`. Since `PanGestureRecognizer` typically expects `TouchPoints == 1`, the recognizer never matches and `PanUpdated` is not raised. This change prevents removing the pointer prematurely when a gesture interaction has not fully completed, ensuring `_fingers` remains consistent until the gesture lifecycle finishes. After this change, dragging from the edge of a control correctly triggers `PanUpdated` as expected. ### Tested Reproduced using a simple `AbsoluteLayout` with a draggable view and `PanGestureRecognizer`. Before fix: - Starting the drag from the edge of the control sometimes prevented `PanUpdated` from firing. After fix: - `PanUpdated` fires consistently when dragging from any part of the control, including edges.
…et#34287) ### Description of Change Removed permission request for adding photos to the devices library on iOS, inside MediaPicker.ios, which doesn't need this permission at this spot inside the code as it does not add the newly captured photo to the device's library here. Given the [official documentation](https://learn.microsoft.com/en-us/dotnet/maui/platform-integration/device-media/picker?view=net-maui-10.0&tabs=windows#take-a-photo), adding the picture to the device's library is an action given in the example _after_ the CapturePhotoAsync is called, if the task's result contains a value. This action is completely optional, the developer could implement different logic for handling the picture data after calling CapturePhotoAsync, meaning the permission request becomes unnecessary and redundant. It is up to the developer to save the image to the device, which then requires the permission, meaning the developer should implement the permission request within their own logic. ### Issues Fixed Fixes dotnet#34246
…iew Items When PointerOver Is Applied (dotnet#29815) <!-- 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! ### Root Cause : The `ChangeVisualState` method does not account for the `Selected` state in its state resolution logic. It prioritizes PointerOver over Selected, and defaults to Normal when no pointer interaction exists—even if the item is selected. This results in inconsistent visual feedback where the Selected state styling is ignored or overridden. ### Description of Change Enhanced `IsElementInSelectedState`() extension to VisualElement that properly detects if an element is currently in the Selected visual state by inspecting the CurrentState property of all visual state groups. Updated `ChangeVisualState`() to prioritize Selected over PointerOver and Normal: When an element is enabled, it first checks if the element is in the Selected state - If the elements with the pointer over them, it applies either the `Selected` or `PointerOver` state, depending on selection status - If the elements are without the pointer over them, it applies either the `Selected` or `Normal` state, depending on the selection status This change ensures a consistent visual representation of `selected` items while preserving pointer and focus behavior. ### Issues Fixed Fixes dotnet#29484 **Tested the behaviour in the following platforms** - [x] Android - [x] Windows - [x] iOS - [x] Mac ### Output | Before| After| |--|--| | <video src="https://github.com/user-attachments/assets/f2f82f55-7501-4560-9b80-162b9f923f0a"> | <video src="https://github.com/user-attachments/assets/88b33563-f165-4271-bf04-97a57305d6ab"> | <video src="https://github.com/user-attachments/assets/2649e2bc-69bc-48a4-b825-0d7169a62c0d"> | <video src="https://github.com/user-attachments/assets/d531d7ac-efee-4e8a-b1f9-7235a303c487"> |
### Description of Change Using the delta from ManipulationDeltaRoutedEventArgs for cumulative displacement generates invalid event data. Consider the following sequence for X coordinates moving the mouse from left to right: x = 0; delta = 0; => totalX = 0; x = 10; delta = 10; => totalX = 20; x = 11; delta = 1; => totalX = 12; On low end hardware or complex scenes, this causes the pan to jump around when dragging. ### Issues Fixed Fixes: dotnet#33539 Co-authored-by: Jakub Florkowski <42434498+kubaflo@users.noreply.github.com>
🤖 AI Summary📊 Expand Full Review —
|
| # | Source | Approach | Test Result | Files Changed | Notes |
|---|---|---|---|---|---|
| PR | PR #28321 | Track Android RecyclerView scroll state and suppress threshold dispatch until scrolling has actually started; add Android UI repro/test coverage. | ⏳ PENDING (Gate) | src/Controls/src/Core/Handlers/Items/Android/RecyclerViewScrollListener.cs, src/Controls/src/Core/PublicAPI/net-android/PublicAPI.Unshipped.txt, src/Controls/tests/TestCases.HostApp/Issues/Issue28321.xaml, src/Controls/tests/TestCases.HostApp/Issues/Issue28321.xaml.cs, src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue28321.cs |
Original PR; review comments still question test naming/cleanup. |
🚦 Gate — Test Verification
Result:
Gate Result: ⚠️ SKIPPED
Platform: android
Mode: Full Verification
- Tests FAIL without fix: not verified locally
- Tests PASS with fix: not verified locally
Notes:
- Required gate execution was attempted twice via task agent.
- Attempt 1 failed because the verification skill auto-detected thousands of unrelated changed files from the review branch diff and never reached test execution.
- Attempt 2 retried with explicit fix file context, but the skill invocation environment did not pass the explicit
FixFilesparameter through to the script, so it fell back to the same invalid auto-detection path. - Prior PR discussion includes an AI summary claiming Android full verification passed, but this review run could not independently reproduce that result due to the environment blocker above.
🔧 Fix — Analysis & Comparison
Fix Candidates
| # | Source | Approach | Test Result | Files Changed | Notes |
|---|---|---|---|---|---|
| 1 | try-fix (claude-opus-4.6) |
Query RecyclerView.ScrollState directly in OnScrolled and only evaluate threshold while non-idle, keeping existing deferred idle dispatch. |
✅ PASS | src/Controls/src/Core/Handlers/Items/Android/RecyclerViewScrollListener.cs, src/Controls/src/Core/PublicAPI/net-android/PublicAPI.Unshipped.txt |
Simpler than PR boolean tracking; uses native scroll state as source of truth. |
| 2 | try-fix (claude-sonnet-4.6) |
Skip threshold evaluation on zero-displacement OnScrolled callbacks (dx == 0 && dy == 0) while leaving idle dispatch behavior intact. |
✅ PASS | src/Controls/src/Core/Handlers/Items/Android/RecyclerViewScrollListener.cs |
Smallest passing change so far; fully stateless. |
| 3 | try-fix (gpt-5.3-codex) |
Skip threshold evaluation on the first OnScrolled callback after item count transitions from zero to non-zero. |
❌ FAIL | src/Controls/src/Core/Handlers/Items/Android/RecyclerViewScrollListener.cs |
Not empirically validated; test command failed before execution due to missing/up-to-date MSBuild task prerequisite. |
| 4 | try-fix (gemini-3-pro-preview) |
Track adapter item count and suppress threshold evaluation on the initial 0→N load transition, while retaining deferred idle dispatch. | ✅ PASS | src/Controls/src/Core/Handlers/Items/Android/RecyclerViewScrollListener.cs, src/Controls/src/Core/PublicAPI/net-android/PublicAPI.Unshipped.txt |
Passing alternative; also surfaced that current PR diff duplicates OnScrollStateChanged and duplicates its PublicAPI entry. |
| 5 | try-fix round 2 (claude-opus-4.6) |
Arm threshold logic only after the visible range moves beyond its initial layout snapshot. | ✅ PASS | src/Controls/src/Core/Handlers/Items/Android/RecyclerViewScrollListener.cs, src/Controls/src/Core/PublicAPI/net-android/PublicAPI.Unshipped.txt |
Distinct passing alternative based on visible-range movement rather than scroll state or displacement. |
| 6 | try-fix round 2 (claude-sonnet-4.6) |
Skip threshold evaluation while recyclerView.IsInLayout is true. |
✅ PASS | src/Controls/src/Core/Handlers/Items/Android/RecyclerViewScrollListener.cs, src/Controls/src/Core/PublicAPI/net-android/PublicAPI.Unshipped.txt |
Directly targets layout-driven callbacks from initial population. |
| 7 | try-fix round 2 (gpt-5.3-codex) |
Require explicit touch intent from MauiRecyclerView before threshold evaluation can run. |
❌ FAIL | src/Controls/src/Core/Handlers/Items/Android/RecyclerViewScrollListener.cs, src/Controls/src/Core/Handlers/Items/Android/MauiRecyclerView.cs |
Could not be empirically validated because Android build failed in unrelated EntryHandler2.Android.cs before test execution. |
| PR | PR #28321 | Track Android RecyclerView scroll state with a _scrollStarted field and suppress threshold dispatch until scrolling has started; add Android UI repro/test coverage. |
src/Controls/src/Core/Handlers/Items/Android/RecyclerViewScrollListener.cs, src/Controls/src/Core/PublicAPI/net-android/PublicAPI.Unshipped.txt, src/Controls/tests/TestCases.HostApp/Issues/Issue28321.xaml, src/Controls/tests/TestCases.HostApp/Issues/Issue28321.xaml.cs, src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue28321.cs |
Local gate blocked by verification script environment; PR also has unresolved review comments on test numbering/cleanup, and the current diff shape duplicates OnScrollStateChanged plus its PublicAPI entry. |
Cross-Pollination
| Model | Round | New Ideas? | Details |
|---|---|---|---|
| claude-opus-4.6 | 1 | N/A | Independent attempt completed. |
| claude-sonnet-4.6 | 1 | N/A | Independent attempt completed. |
| gpt-5.3-codex | 1 | N/A | Independent attempt hit build prerequisite blocker before test execution. |
| gemini-3-pro-preview | 1 | N/A | Independent attempt completed. |
| claude-opus-4.6 | 2 | Yes | Arm threshold only after visible range changes from the initial snapshot. |
| claude-sonnet-4.6 | 2 | Yes | Gate threshold evaluation on !recyclerView.IsInLayout. |
| gpt-5.3-codex | 2 | Yes | Gate threshold evaluation on explicit touch input. |
| gemini-3-pro-preview | 2 | No | NO NEW IDEAS |
| claude-opus-4.6 | 3 | Yes | Arm threshold only after cumulative post-load scroll distance exceeds touch slop. |
| claude-sonnet-4.6 | 3 | Yes | Suppress threshold evaluation while ItemAnimator is running. |
| gpt-5.3-codex | 3 | Yes | Delay attaching the scroll listener until after initial global layout. |
| gemini-3-pro-preview | 3 | Yes | Skip threshold evaluation when scroll range changed due to content-size change. |
Exhausted: Yes (max 3 rounds reached)
Selected Fix: Candidate #2 — it is the simplest passing fix, requires only a tiny localized change in RecyclerViewScrollListener, introduces no additional state or listener wiring, and still directly blocks layout/data-load callbacks from triggering threshold logic.
📋 Report — Final Recommendation
Result:
⚠️ Final Recommendation: REQUEST CHANGES
Phase Status
| Phase | Status | Notes |
|---|---|---|
| Pre-Flight | ✅ COMPLETE | Issue/PR discussion reviewed; implementation/test files classified. |
| Gate | Android verification skill was attempted twice but blocked by diff/parameter-passing issues in this environment. | |
| Try-Fix | ✅ COMPLETE | 7 attempts total, 5 passing alternatives, 3 cross-pollination rounds completed (max reached). |
| Report | ✅ COMPLETE |
Summary
This PR addresses a real Android regression, and the added repro coverage is directionally correct, but I do not recommend approval in its current state.
The strongest issue is correctness of the current diff itself: src/Controls/src/Core/Handlers/Items/Android/RecyclerViewScrollListener.cs contains two OnScrollStateChanged(RecyclerView, int) overrides, and src/Controls/src/Core/PublicAPI/net-android/PublicAPI.Unshipped.txt contains the same OnScrollStateChanged API entry twice. That is a concrete defect in the submitted change, independent of the local gate blocker.
Beyond that, try-fix exploration found multiple simpler passing alternatives. The best validated option was candidate #2: ignore threshold evaluation for zero-displacement OnScrolled callbacks (dx == 0 && dy == 0), which solves the repro with a much smaller, stateless change than the PR’s _scrollStarted tracking.
The PR also still carries unresolved review feedback on the test assets: the added issue page/test are numbered after PR 28321 rather than linked issue 28279, and the HostApp code-behind includes an unused Data record.
Root Cause
Android RecyclerView emits OnScrolled callbacks during initial layout/data population even when no user scroll has occurred. The threshold logic currently treats those callbacks as eligible for RemainingItemsThresholdReached, so the command can fire on first load.
Fix Quality
The PR fix is plausible in intent, but the current implementation is not ready:
- it introduces duplicate override/API declarations in the current diff,
- it uses extra state where several smaller passing alternatives exist,
- and the test additions still need cleanup/alignment with the linked issue.
Given the duplicate method/API entries plus the existence of simpler validated alternatives, this should be revised before approval.
6e563dc to
00535e4
Compare
Code Review — PR #28321Independent AssessmentWhat this changes: Adds a Inferred motivation: Android's Reconciliation with PR NarrativeAuthor claims: Fixes #28279 — Findings❌ Critical — Duplicate
|
🟡 .NET MAUI Review - Changes SuggestedExpand Full Review -
|
|
Not needed anymore |
Issues Fixed
Fixes #28279
Screen.Recording.2025-03-11.at.18.24.14.mov
Screen.Recording.2025-03-11.at.18.23.26.mov
Screen.Recording.2025-03-11.at.18.25.29.mov