[automated] Merge branch 'main' => 'net11.0'#35001
Closed
github-actions[bot] wants to merge 220 commits intonet11.0from
Closed
[automated] Merge branch 'main' => 'net11.0'#35001github-actions[bot] wants to merge 220 commits intonet11.0from
github-actions[bot] wants to merge 220 commits intonet11.0from
Conversation
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…#34265) <!-- 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! ## Summary Adds a standalone `code-review` skill for reviewing PR code changes with MAUI-specific domain knowledge, modeled after [dotnet/android's android-reviewer skill](https://github.com/dotnet/android/tree/main/.github/skills/android-reviewer). ### What's included **`.github/skills/code-review/SKILL.md`** (196 lines) — Review workflow: - Independence-first methodology (read code before PR description to avoid anchoring bias) - 6-step workflow: gather context → load rules → independent assessment → reconcile with PR narrative → check CI → devil's advocate verdict - Three verdicts: `LGTM`, `NEEDS_CHANGES`, `NEEDS_DISCUSSION` - Optional multi-model review for diverse perspectives - Output constraints: don't pile on, don't flag what CI catches **`.github/skills/code-review/references/review-rules.md`** (345 lines) — Domain knowledge: - **22 sections** of review rules distilled from real FTE code reviews - **138 PR citations** linking each rule to the PR where the pattern was identified - Sourced from **366 PRs**: top 142 most-discussed PRs (2,883 review comments), 30 reverted PRs, 50 candidate-branch failures, 64 regression fixes - Covers: handler lifecycle, memory leaks, safe area, layout, platform code, Android/iOS/Windows specifics, navigation, CollectionView, threading, XAML, testing, performance, error handling, API design, images, gestures, build, accessibility - §21 Regression Prevention — lessons from reverts and candidate failures - §22 Component Risk Table — ranking the most regression-prone components ### Design decisions - **Separation of concerns**: SKILL.md = workflow, review-rules.md = knowledge (follows Android's pattern) - **Standalone**: Can be invoked directly by users or by other agents. No coupling to the PR agent pipeline. - **Data-driven**: Every rule traces to a real PR where a senior maintainer flagged the pattern. No generic advice. ### Changes to copilot-instructions.md - Added "Opening PRs" section with required NOTE block template - Added code-review skill to the skills listing - Minor reformatting of existing documentation sections --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: Jakub Florkowski <kubaflo123@gmail.com>
… a footer template is present or crashes when removing data. (#24867) ### Root Cause - **Add** - When a CollectionView contains grouping and a footer template, we create a new List<object> and add the footer to the list if the footer's item template type is not an IList. This prevents events such as CollectionChanged or INotifyCollectionChanged from being triggered on the original collection. As a result, when a new item is added to the collection, it is not reflected in the view. - **Clear** - When clearing the collection and adding items at runtime, a COM exception occurs when items are grouped. This happens because the OnItemsVectorChanged event is triggered whenever there is a change in the item collection, causing ListViewBase.ScrollIntoView to be called from a background thread, which leads to the COM exception. ### Description of Change #### Windows - **Add** - Reverting the changes made in PR #24205 caused the exception . This exception occurred due to the triggering of the TemplateCollectionChanged event, which was introduced during the implementation of drag-and-drop functionality for the CollectionView in PR #3768. Adding the footer to the items triggers the TemplateCollectionChanged event, which incorrectly adds the footer as a new item in the collection. Since the footer template is not the same type as the items in the original collection, this results in the exception. To resolve this, we can ignore the addition of the footer template to the ItemsSource. - **Clear** - Calling ListViewBase.ScrollIntoView in the DispatcherQueue helps avoid exceptions and ensures synchronized updates. Reference - https://github.com/dotnet/maui/blob/5ceff42f4713a18516a0bffa209b0ed272e915b7/src/Controls/src/Core/Compatibility/Handlers/ListView/Windows/ListViewRenderer.cs#L628 Validated the behaviour in the following platforms - [x] Android - [x] Windows - [ ] iOS - [ ] Mac iOS and Mac has issues - #17969 ### Issues Fixed Fixes #24866 Fixes #27302 Fixes #18481 Fixes #21791 ### Output #### Windows |**Before**|**After**| |--|--| |<video src="https://github.com/user-attachments/assets/2813d618-2c9d-4700-af20-8402b0779597"> | <video src="https://github.com/user-attachments/assets/bb96519e-5a70-47a8-a872-d61ac2cf8f4f"> |
… value on iOS16+ - fix (#30733) <!-- 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. !!!!!!! --> ### Issues Fixed Fixes #18669 |Before|After| |--|--| |<video src="https://github.com/user-attachments/assets/1c43b0dc-a432-4107-81b5-d418e0a70a7b" width="300px"></video>|<video src="https://github.com/user-attachments/assets/fff7b26c-e703-4761-baa1-bf3e60f01005" width="300px"></video>|
… fix (#29339) <!-- 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! ### Issues Fixed Fixes #20596 |Before|After| |--|--| |<video src="https://github.com/user-attachments/assets/dd0ce289-bbff-453c-81ff-5306c891b14e" width="300px"/>|<video src="https://github.com/user-attachments/assets/8b07e943-4d38-4575-a216-e1252d12785c" width="300px"/>|
…ork in CollectionView after scroll - fix (#28151) ### Issues Fixed Fixes #28147 |Before|After| |--|--| |<video src="https://github.com/user-attachments/assets/09a75432-4b03-465a-910d-844076f18548" width="300px"/>|<video src="https://github.com/user-attachments/assets/29d23e8c-6eaf-45c0-a6ed-e90ffc089c52" width="300px"/>|
…ft Text Selection on Editor and Entry (#30906) <!-- 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. !!!!!!! --> ### Issue Details When selecting text from right to left, the SelectionLength property in Entry and Editor is incorrectly reported as 0 on Android. ### Root Cause On Android, when selecting text from right to left, GetSelectedTextLength returned 0 due to incorrect handling of reverse selection. In GetSelectionEnd, the selection length was also calculated as 0, resulting in the selection length remaining zero even though text was selected. ### Description of Change Updated GetSelectedTextLength() to return the absolute difference between SelectionStart and SelectionEnd using Math.Abs. Modified GetSelectionEnd() to handle right-to-left selection by adjusting the end position and correctly updating SelectionLength. This ensures consistent behavior across selection directions and platforms. ### Validated the behaviour in the following platforms - [x] Android - [x] Windows - [x] iOS - [x] Mac ### Issues Fixed: Fixes #30782 ### Screenshots | Before | After | |---------|--------| | <video src="https://github.com/user-attachments/assets/9e6aff76-aa74-41f5-9047-e8449262b313"> | <video src="https://github.com/user-attachments/assets/e744b5fd-0b98-40d1-8875-e36e64021ccc"> |
…PopToRootAsync (#29779) ### Issue Detail When the user inserts a page before the root and then pops the current page using PopAsync (or pops to root using PopToRootAsync), the flyout hamburger icon is not visible on iOS after the navigation completes. ### Root Cause UpdateLeftBarButtonItem (which sets the flyout hamburger icon on the ParentingViewController) was only called in the RemovePage method. The programmatic pop paths — OnPopViewAsync, OnPopToRoot, and the native-swipe-back path via UpdateFormsInnerNavigation — did not call it. After a pop that results in a new root page, the flyout button was never refreshed. ### Description of Change Extracted the flyout-button-refresh logic from RemovePage into a new private helper: ``` void UpdateFlyoutMenuButton(Page pageBeingRemoved = null) { var parentingViewController = GetParentingViewController(); parentingViewController?.UpdateLeftBarButtonItem(pageBeingRemoved); } ``` Then called UpdateFlyoutMenuButton() at the end of: - OnPopViewAsync — programmatic PopAsync() - OnPopToRoot — programmatic PopToRootAsync() - UpdateFormsInnerNavigation — native back-button (swipe) pop RemovePage was refactored to use the same helper (no behavior change). **File changed:** src/Controls/src/Core/Compatibility/Handlers/NavigationPage/iOS/NavigationRenderer.cs **Reference:** https://github.com/dotnet/maui/blob/main/src/Controls/src/Core/Compatibility/Handlers/NavigationPage/iOS/NavigationRenderer.cs#L693 ### Tested the behavior in the following platforms - [x] Android - [x] Windows - [x] iOS - [x] Mac ### Issues Fixed Fixes #21828 ### Screenshots | Before Issue Fix | After Issue Fix | |----------|----------| | <video width="300" height="600" src="https://github.com/user-attachments/assets/7afc9d80-3d98-4f73-a124-dcc06c5e6d1c"> | <video width="300" height="600" src="https://github.com/user-attachments/assets/7d59f508-9760-44bb-9889-4bd4fcca9353">) |
…ource loads with delay (#34424) > [!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 bug is a **circular sizing dependency** specific to iOS horizontal `CollectionView` with delayed data loading: 1. **UIKit assigns a large initial frame** (~812–1013px) to the `CollectionView` before items load. 2. **`LayoutFactory2.CreateHorizontalList`** configures item and group heights as `NSCollectionLayoutDimension.CreateFractionalHeight(1f)` — items always fill 100% of the container height. This is intentional for horizontal scroll UX. 3. Before items are realized, `CollectionViewContentSize.Width == 0` but **`CollectionViewContentSize.Height` equals the current (large) frame height** — a side effect of `FractionalHeight(1f)`. 4. In **`EnsureContentSizeForScrollDirection`** (CV2 / `ItemsViewHandler2.iOS.cs`), the `Width == 0` branch correctly fetches a desired width from `base.GetDesiredSize()` — but previously did NOT reset `Height`, letting the large frame-derived height pass through. 5. MAUI reports this large height as the desired size → sets the frame to that height → when items finally load, `FractionalHeight(1f)` × large frame = items are large → height is never corrected. **The lock is permanent.** The same bug exists in the legacy CV1 handler (`ItemsViewController.cs`) where `GetSize()` returned the raw `CollectionViewContentSize` without resetting height for an empty horizontal layout. ### Description of Change In both the CV2 (`Items2/`) and CV1 (`Items/`) iOS handler pipelines, when a **horizontal CollectionView has `Width == 0`** (meaning no items have been realized yet), also reset `Height = 0`. This breaks the circular dependency by letting `MinimumHeightRequest` / `HeightRequest` govern the initial size rather than an arbitrary frame-derived value. **The condition `Width == 0`** is a reliable proxy for "no items realized" because: - A horizontal CV with any visible items will always have non-zero content width. - `Width == 0` uniquely occurs in the pre-items-loaded state that causes the bug. ### Issues Fixed Fixes #34336 ### Technical Details - **CV2 (`ItemsViewHandler2.iOS.cs`)** — When a horizontal `CollectionView` has no items loaded yet (`Width == 0`), `contentSize.Height` was incorrectly carrying the large UIKit-assigned frame height. The fix also resets `Height` to zero in that case, so `MinimumHeightRequest` / `HeightRequest` governs the initial size. - **CV1 (`ItemsViewController.cs`)** — Same issue in the legacy handler. A guard was added: if the layout is horizontal and `Width == 0`, `Height` is reset to zero before the size is returned. ### Files Changed | File | Change | Purpose | |------|--------|---------| | `src/Controls/src/Core/Handlers/Items2/ItemsViewHandler2.iOS.cs` | +8 lines | Fix CV2 (new iOS handler) circular height lock | | `src/Controls/src/Core/Handlers/Items/iOS/ItemsViewController.cs` | +15/-1 lines | Fix CV1 (legacy iOS handler) circular height lock | | `src/Controls/tests/TestCases.HostApp/Issues/Issue34336.cs` | New file (104 lines) | HostApp repro page: horizontal CV with 1s delayed `ObservableCollection` load | | `src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue34336.cs` | New file (31 lines) | NUnit UI test using `VerifyScreenshot(tolerance: 0.5)` to verify correct layout after delayed load | ### Screenshots | Before Issue Fix | After Issue Fix | |----------|----------| | <video width="300" height="600" src="https://github.com/user-attachments/assets/ce13c0b9-cd35-40fe-a341-bb3c634cd441"> | <video width="300" height="600" src="https://github.com/user-attachments/assets/49f720d6-b8ca-4520-bbe7-d579a9b43d18">) |
<!-- 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! ### Description of Change This pull request introduces a new Material 3-based `SearchBarHandler2` for Android, and conditionally uses it when Material 3 is enabled. The new handler and its supporting platform classes provide improved integration with Material 3 design, including custom appearance, clear button, and color handling. Several internal extension methods and mappings are updated to support this new handler, while maintaining compatibility with the existing handler for non-Material 3 scenarios. **Material 3 SearchBar support for Android:** * Added a new internal `SearchBarHandler2` class for Material 3, with a complete set of property and command mappers, event handlers, and platform view integration. This handler is used only when Material 3 is enabled. * **SearchView limitation:** Android’s SearchView is designed to open as an expanded overlay when tapped, covering other content. It's meant to work in combination with a collapsed search bar trigger. * The overlay’s height and behavior cannot be customized to behave like a persistent inline search field. This limitation is inherent to the SearchView design. * **Solution**: So Introduced `MauiMaterialTextInputLayout` and `MauiMaterialTextInputEditText` platform classes to provide Material 3 styling, clear button, and selection change support for the new SearchBar handler. * Updated `AppHostBuilderExtensions.cs` to register `SearchBarHandler2` conditionally for Android when Material 3 is enabled, otherwise falling back to the original handler. **Mapping and platform extension updates:** * Modified SearchBar mapping logic in `SearchBar.Mapper.cs` to use the correct handler and mapping methods based on whether Material 3 is enabled on Android. * Added new extension methods in `SearchViewExtensions.cs` for Material 3 SearchBar support, including updating text, background, icon colors, and return type for the new handler. * Added a Material 3-specific overload for `MapText` in `SearchBar.Android.cs` for use with `SearchBarHandler2`. **Internal API and behavior improvements:** * Made `UpdateIsTextPredictionEnabled` and `UpdateIsSpellCheckEnabled` extension methods internal to allow usage by the new handler. * Improved `UpdateCursorSelection` in `EditTextExtensions.cs` to post selection changes when the EditText is focused, preventing potential issues with selection updates. ### Issues Fixed Fixes #33947 ### Screenshot | Material 2 | Material 3 | |---------|--------| | <img height=600 width=300 src="https://github.com/user-attachments/assets/75269b67-1b8c-4e31-a880-c86d4102c76c"> | <img height=600 width=300 src="https://github.com/user-attachments/assets/ba76f61d-587f-4421-b782-24bfb7a74155"> | --------- Co-authored-by: Jakub Florkowski <42434498+kubaflo@users.noreply.github.com>
<!-- 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. !!!!!!! --> ### Issue Details The sizing of the RefreshView no longer works when the content is wrapped inside a RefreshView. As a result, the RefreshView height is calculated as 0. ### Root Cause MauiSwipeRefreshLayout.OnMeasure called CrossPlatformMeasure() but discarded the return value, never calling SetMeasuredDimension with the correct size. Android kept height=0 from base.OnMeasure(). ### Description of Change Updated MauiSwipeRefreshLayout.OnMeasure() to correctly use the result from CrossPlatformMeasure() and call SetMeasuredDimension() with the resolved size. Previously, the return value was discarded and Android retained height=0 from base.OnMeasure(). The implementation respects MeasureSpecMode as follows: - EXACTLY — uses the size from the measure spec (parent dictates size). - AT_MOST — uses the full spec constraint (not the cross-platform measure), matching Android's View.getDefaultSize() semantics. This is intentional: SwipeRefreshLayout internally centers its spinner indicator using getMeasuredWidth() / 2, so clamping to content size would misposition the spinner. - UNSPECIFIED — uses the size returned from CrossPlatformMeasure() (content wraps naturally). ### Validated the behaviour in the following platforms - [x] Android - [ ] Windows - [ ] iOS - [ ] Mac ### Issues Fixed: Fixes #12131 ### Screenshots | Before | After | |---------|--------| | <img height=600 width=300 src="https://github.com/user-attachments/assets/6ffdfc04-fdc0-4c1a-b0fd-3f67c4744571"> | <img height=600 width=300 src="https://github.com/user-attachments/assets/7143297a-94f2-48a8-9eca-3516ddf70491"> |
<!-- 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 : `SendNavigatingFrom()` was called too early in `SendNavigating()`—before the navigation stack updated. At that point, `CurrentPage` still pointed to the source page, so the event incorrectly reported the source page as the destination. ### Description of Change Bug fix for navigation event: * Updated `IShellController.UpdateCurrentState` in `Shell.cs` to fire `SendNavigatingFrom` after the shell state is updated, ensuring `CurrentPage` correctly reflects the destination page in the `NavigatingFromEventArgs`. The navigation type is determined based on the navigation source. * Modified `SendNavigating` in `Shell.cs` to capture the current page for later use, rather than immediately firing `SendNavigatingFrom`, so the destination is correct when the event is triggered. Test coverage for the fix: * Added a new test case in `Issue34073.cs` under `TestCases.HostApp` to demonstrate and validate the correct behavior of `OnNavigatingFrom`, showing the destination page is now accurate. * Added a UI test in `TestCases.Shared.Tests/Tests/Issues/Issue34073.cs` that navigates between pages and asserts that the destination page reported by `OnNavigatingFrom` is correct. <!-- 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 #34073 ### Tested the behavior in the following platforms - [x] Windows - [x] Android - [x] iOS - [x] Mac | Before Issue Fix | After Issue Fix | |----------|----------| | <img width="1080" height="1920" alt="BeforeFix34073" src="https://github.com/user-attachments/assets/763e1c20-9f41-4fc2-b7d6-21261280b42a" /> | <img width="1080" height="1920" alt="AfterFix34073" src="https://github.com/user-attachments/assets/bc4f998e-52b7-492f-8fc5-61e2f56881a4"> | | <img width="1125" height="2436" alt="BeforeFixiOS34073" src="https://github.com/user-attachments/assets/fadc8294-77a8-4db2-b48f-787eb49e0f8b" /> | <img width="1125" height="2436" alt="AfterFixiOS34073" src="https://github.com/user-attachments/assets/237f1fbf-649a-48d2-9930-a182695cf9e9" /> | <!-- Are you targeting main? All PRs should target the main branch unless otherwise noted. -->
…anges (#34416) ### Root Cause On Android, MAUI declares `configChanges="density"` in the manifest. As a result, when the user changes the system display size (display density), the `Activity` is not recreated. Instead, the existing view hierarchy remains active and `OnSizeChanged` is triggered to update the view’s pixel dimensions. However, `PlatformGraphicsView` initializes its internal `_scale` field only once during construction, and the field is marked as `readonly`. Because the `Activity` is not recreated, this value is never refreshed after a density change. This leads to a mismatch between two density sources: * `GraphicsView.Width` and `GraphicsView.Height`, which rely on MAUI’s cached display density (`s_displayDensity`) set at application startup. * The `dirtyRect` passed into `IDrawable.Draw()`, which is calculated using the stale `_scale` value from `PlatformGraphicsView`. After a system display size change, these two values diverge. As a result, the drawable’s coordinate space no longer aligns with the view’s logical dimensions, causing rendering inconsistencies and incorrect drawing behavior. ### Description of Change Removed the `readonly` modifier from the `_scale` field in `PlatformGraphicsView` and introduced an internal virtual `GetDisplayDensity()` method. The `_scale` value is now refreshed on every `OnSizeChanged` call using the value returned by this method, ensuring it reflects the current display density. `PlatformTouchGraphicsView` (the MAUI-integrated subclass in the Core project) overrides `GetDisplayDensity()` to return MAUI’s cached display density — the same value used by `GraphicsView.Width` and `GraphicsView.Height`. This ensures that both logical sizing and `dirtyRect` calculations rely on a consistent density source, keeping them fully synchronized even after a system display density change. ### Issues Fixed Fixes #34211 Tested the behaviour in the following platforms - [x] Android - [x] Windows - [x] iOS - [x] Mac ### Note display density can be forced on Android using `adb shell wm density`. Other platforms do not provide a comparable runtime command or automation method to override display density, so this scenario cannot be triggered or validated the same way outside Android. ### Output Video Before Issue Fix | After Issue Fix | |----------|----------| |<video width="40" height="60" alt="Before Fix" src="https://github.com/user-attachments/assets/6c6af647-c758-40b3-802c-000d5021a936">|<video width="50" height="40" alt="After Fix" src="https://github.com/user-attachments/assets/85f7b299-2349-4fef-a2c1-b68b485dbd34">|
…WebView and HybridWebView (#30653) <!-- 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. !!!!!!! --> ### Issue Details FlowDirection was only applied to the WebView and HybridWebview, not its internal ScrollView, which handles the actual content layout and scrolling. ### Description of Change <!-- Enter description of the fix in this section --> FlowDirection is now applied to the internal ScrollView of WKWebView to ensure correct layout direction. ### 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 #30605 ### Regarding Test case For this case, it's not possible to write a test because the scrollbar does not appear during initial loading. It only becomes visible when a scroll action is performed, and even then, it remains visible only for a few seconds. <!-- 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 | |---------|--------| | **iOS**<br> <video src="https://github.com/user-attachments/assets/eaf6620b-5f00-402c-b191-2ba881cacac2" width="300" height="600"> | **iOS**<br> <video src="https://github.com/user-attachments/assets/b038125e-5dbf-483b-aa7d-640f2c72555e" width="300" height="600"> |
#33439) <!-- !!!!!!! MAIN IS THE ONLY ACTIVE BRANCH. MAKE SURE THIS PR IS TARGETING MAIN. !!!!!!! --> ### Issue Details MediaPicker on Android was appending _rotated and _processed suffixes to filenames when rotating or compressing images, causing the original filename to be lost. ### Description of Change <!-- Enter description of the fix in this section --> The updated implementation closes the input stream before performing any file operations, deletes the original file, and writes the processed (rotated) image back to the same file path. This ensures that the original filename is preserved, avoiding the addition of any _rotated or _processed suffixes. ### 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 #33258 ### Regarding test case Picking an image is not possible in a test. <!-- Are you targeting main? All PRs should target the main branch unless otherwise noted. --> **Tested the behavior in the following platforms.** - [x] Android - [ ] Windows - [ ] iOS - [ ] Mac | Before | After | |---------|--------| | **Android**<br> <video src="https://github.com/user-attachments/assets/35cb8242-9d10-4926-81b8-d28e291a56fe" width="300" height="600"> | **Android**<br> <video src="https://github.com/user-attachments/assets/92249ba3-76af-4d05-a789-8f63d02de86b" width="300" height="600"> |
…iew (#34382) ### Root Cause When `RefreshView` initializes on iOS, it attaches the native `UIRefreshControl` to the CollectionView’s scroll layer. This attachment triggers an internal layout pass. During that layout pass, iOS temporarily reports an incorrect container width to the layout engine, typically double the actual screen width. `UICollectionViewCompositionalLayout`, which is used by the CV2 handler, uses this incorrect width to calculate the total content size and caches it. After the animation settles and the correct screen width is restored, the layout engine does not recalculate the content size and continues using the cached double-width value. As a result, the scroll system believes the content is wider than the screen and enables horizontal scrolling. ### Description of Change The fix was implemented by overriding the `CollectionViewContentSize` property inside `CustomUICollectionViewCompositionalLayout`. This property is where the scroll system queries the layout for the total content size. The override intercepts the content size before it is returned to the scroll system. If the layout is vertical and the reported content width is greater than the actual screen width, the width is clamped to the real screen width before being returned. By ensuring that a vertical layout never reports a content width larger than the screen, horizontal scrolling is prevented. The change is minimal, localized to a single file, and safe because a vertical layout can never legitimately have content wider than the screen. ### Issues Fixed Fixes #34165 Tested the behaviour in the following platforms - [x] Android - [x] Windows - [x] iOS - [x] Mac ### Output Video Before Issue Fix | After Issue Fix | |----------|----------| |<video width="40" height="60" alt="Before Fix" src="https://github.com/user-attachments/assets/0acc28a6-a526-4799-8de7-99c4b0dbf4fe">|<video width="50" height="40" alt="After Fix" src="https://github.com/user-attachments/assets/61100874-a442-4a50-96ee-0539a2544ac3">|
…dices with grouped items (#34240) > [!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 a CollectionView's IsGrouped property is set to true, the values of FirstVisibleItemIndex, CenterItemIndex, and LastVisibleItemIndex in the ItemsViewScrolledEventArgs passed to the Scrolled event handler are incorrect. ### Root Cause - Visible items were sorted only by Row, which produced incorrect ordering when items from multiple sections were visible simultaneously. ### Description of Change **iOS (ItemsViewDelegator.cs and ItemsViewDelegator2.cs)** - Changed the sort from .OrderBy(x => x.Row) to .OrderBy(x => x.Section).ThenBy(x => x.Row) so that IndexPathsForVisibleItems is ordered correctly across section boundaries. The fix is applied to both the legacy handler (Items/) and the current handler (Items2/). ### Issues Fixed Fixes #17664 ### Validated the behaviour in the following platforms - [x] Windows - [x] Android - [x] iOS - [x] Mac Android fix PR: #31437 ### Output | Before | After | |----------|----------| | <video src="https://github.com/user-attachments/assets/3612815d-3a43-4dd5-8d2c-3832e3d4a077"> | <video src="https://github.com/user-attachments/assets/f6cd63b8-1ed9-465a-99d9-11e38b004dac"> |
) <!-- 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. !!!!!!! --> ### Issue Details Label.Clip did not clip the background on iOS and Windows when `Background` was also set. The background rendered outside the clipping geometry. ### Root Cause When a Label has a non-null Background, LabelHandler.NeedsContainer returns true, wrapping the label in a WrapperView. **iOS**: MapBackground called handler.ToPlatform(), which returns the WrapperView when a container is present. The background was applied to the wrapper, but WrapperView.SetClip() only masks sublayers named MauiBackgroundLayer — solid colors set via BackgroundColor are not sublayers, so they were never masked. ### Description of Change **iOS**: Changed MapBackground to use handler.PlatformView?.UpdateBackground(label) (routes to MauiLabel) instead of handler.ToPlatform() (routes to WrapperView). The WrapperView clip then correctly clips the label and its background together. Validated the behavior in the following platforms - [x] Android - [x] Windows - [x] iOS - [x] Mac ### Issues Fixed Fixes #34114 ### Output ScreenShot |Before|After| |--|--| | <video src="https://github.com/user-attachments/assets/415fa851-7a19-4874-8afb-f240d3962067" >| <video src="https://github.com/user-attachments/assets/59c24428-0631-43a9-9720-f22649a20f82">|
…ting 4+ images with CompressionQuality set (#34281) > [!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 from this PR and let us know in a comment if this change resolves your issue. Thank you! ### Issue Details On iOS, when using MediaPicker.PickPhotosAsync() with CompressionQuality enabled and SelectionLimit = 0 (unlimited), selecting several photos returns an empty list instead of the selected photos. ### Root Cause When compression is enabled, the original implementation eagerly loads and processes all selected photos immediately after the picker is dismissed by calling NSItemProvider.LoadDataRepresentationAsync() sequentially in a tight loop. When four or more items are processed in rapid succession, these data loads fail or time out on iOS. As a result, the operation fails and returns an empty list. This occurs because multiple instances cannot reliably load data simultaneously or immediately after the picker view controller is dismissed. ### Description of Change The fix introduces a lazy compression approach using a new PHPickerProcessedFileResult wrapper. Image processing is deferred until the stream is actually opened, avoiding iOS resource constraints caused by eager loading of multiple instances in quick succession. The changes now aligns with MAUI Graphics limitations: image.SaveAsync supports only JPEG and PNG output formats. Therefore, when a non-PNG image is compressed, JPEG is the only valid output format. ### Issues Fixed Fixes #33954 ### Tested platforms - [x] Android - [x] Windows - [x] iOS - [x] Mac **Regarding the test case:** MediaPicker invokes the native iOS photo picker, which is a system-level UI running outside the app process. Because of this, it cannot be controlled or captured by Appium or other automated test frameworks. Therefore, the test case is ignored. **Key changes in `MediaPicker.ios.cs`:** - Added `PHPickerProcessedFileResult` class (~67 lines) — wraps a `FileResult` and defers all NSItemProvider I/O to stream-open time - Modified `PickerResultsToMediaFiles()` — wraps results in `PHPickerProcessedFileResult` instead of eagerly compressing ### Screenshots | Before Issue Fix | After Issue Fix | |----------|----------| | <video width="300" height="600" src="https://github.com/user-attachments/assets/18e6f361-0ba4-4176-9f25-5c0e07a32b3c"> | <video width="300" height="600" src="https://github.com/user-attachments/assets/435083d5-301b-4e8f-9daf-94b324f63356">) |
> [!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 SearchBar.CancelButtonColor has no effect on iOS 26 (Liquid Glass design). On iOS 18.x, the cancel button shows "Cancel" text and CancelButtonColor is correctly applied via SetTitleColor. On iOS 26, Apple redesigned the cancel button — it now shows an "xmark" icon (a UIButton with accessibility label "Close") instead of text. All standard color APIs (SetTitleColor, TintColor, UIButtonConfiguration.BaseForegroundColor) are silently overridden by UIButton's Liquid Glass compositor on every layout pass, so the X icon always renders in the system default color regardless of CancelButtonColor. ### Root Cause UIButton on iOS 26 renders the X icon through its own Liquid Glass compositor — a system-level final rendering pass that runs after all UIKit APIs, CALayer, and CALayer.Filters. The compositor is scoped to UIButton's own view subtree, meaning any color applied inside the button (text color, tint, image, configuration, sublayer) is overridden before it reaches the screen. ### Description of Change On iOS 26, UIButton renders the X icon through the Liquid Glass compositor, which runs after all UIKit APIs and overrides any color set on the button or its subviews. To work around this, instead of coloring the button itself, the fix renders a colored "xmark" icon image using UIGraphicsImageRenderer with CGBlendMode.SourceIn (baking the color into the pixels so UIKit cannot override it) and places it in a separate UIImageView next to the cancel button in the view hierarchy — completely outside UIButton's rendering scope. The overlay is positioned to sit exactly on top of the X icon and has UserInteractionEnabled = false so taps still reach the real cancel button underneath. The cancel button lookup uses FindDescendantView<UIButton> with a predicate btn.FindParent(v => v is UITextField) == null to ensure only the cancel button is matched, excluding the clear button inside the text field. The overlay is deferred via DispatchAsync so the cancel button frame is valid after layout, and a WeakReference<UISearchBar> is used to avoid retain cycles and always look up the current cancel button instance across theme transitions. ### Issues Fixed Fixes #33964 **Regarding the test case:** The [AppTheme feature matrix](https://github.com/dotnet/maui/tree/main/src/Controls/tests/TestCases.HostApp/FeatureMatrix/AppTheme) already covers the SearchBar cancel button color in both light and dark themes through LightTheme_SearchBar_VerifyVisualState and DarkTheme_SearchBar_VerifyVisualState. Therefore, no additional tests are added. ### Files Changed - `src/Core/src/Platform/iOS/SearchBarExtensions.cs` — iOS 26 overlay fix ### Key Technical Details - `CGBlendMode.SourceIn` recolors only the xmark pixels while preserving the icon shape and transparency - `UIImageRenderingMode.AlwaysOriginal` prevents UIKit from re-tinting the baked image after placement - `UserInteractionEnabled = false` on the overlay ensures taps fall through to the real cancel button - `CancelButtonColorOverlayTag = 0x53424343` (encodes "SBCC") identifies the overlay for cleanup on color change or cancel button removal - `WeakReference<UISearchBar>` avoids retain cycles; the cancel button itself is looked up fresh on each dispatch since iOS 26 may recreate it during theme transitions - This change only applies to `OperatingSystem.IsIOSVersionAtLeast(26)` — pre-iOS 26 behavior is unchanged ### Screenshots Light Theme: | Before Issue Fix | After Issue Fix | |----------|----------| |<img width="762" height="1682" alt="Image" src="https://github.com/user-attachments/assets/24e30082-096a-4fc3-b75d-51ca8994e8f5" />|<img width="762" height="1682" alt="Image" src="https://github.com/user-attachments/assets/66c83175-a4a8-40d1-9a9f-6bddb21857e2" />| Dark Theme: | Before Issue Fix | After Issue Fix | |----------|----------| |<img width="762" height="1682" alt="Image" src="https://github.com/user-attachments/assets/1aeb1bac-9c51-4df0-8afe-d189f7fec46c" />|<img width="762" height="1682" alt="Image" src="https://github.com/user-attachments/assets/584b1cd2-ab53-4631-bb1b-626554000591" />|
) This pull request adds a new Shell feature matrix to the test cases host app, providing a dedicated UI for testing and exploring Shell navigation features in .NET MAUI. The main changes introduce new pages for the Shell feature matrix, including a main page and a comprehensive options page to test navigation events and stack manipulation. **Shell Feature Matrix Integration** * Added a new `ShellFeaturePage` to the feature matrix in `CorePageView.cs`, making Shell navigation features accessible from the gallery of test pages. * Implemented `ShellFeaturePage.xaml` and its code-behind, providing a main entry point for Shell navigation testing, including a button to launch the navigation control page. [[1]](diffhunk://#diff-429088ce96d697ab4ebcb64f4f34eab95990318df0e699a206770e487cc5f99cR1-R17) [[2]](diffhunk://#diff-d9fe6832827db8c2b917b1667eb42de532a901608d7f118f002848d9a7fc5018R1-R25) **Shell Navigation Options UI** * Added `ShellNavigationOptionsPage.xaml`, a comprehensive UI for testing Shell navigation options, including navigation events, stack inspection, and tab navigation methods. This page uses data binding to display current navigation state and exposes buttons for navigation stack manipulation. **New Issue Identified** - #34318 **Existing Issue Identified** - [On Windows TextOverride and IconOverride is not working](#1625) https://github.com/user-attachments/assets/9ab48004-334a-45d9-95db-aaf42de2b083 --------- Co-authored-by: Jakub Florkowski <kubaflo123@gmail.com> # Conflicts: # .github/scripts/Review-PR.ps1 # eng/pipelines/ci-copilot.yml
…n called before the view is mounted (#34331) <!-- 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: The issue occurs because CollectionView (Items2 / CollectionViewHandler2) depends on the native UIKit layout pass to compute its content size. When Measure() is called before the view is mounted, the native collection view has not performed layout yet, so ContentSize remains {0,0}. MAUI then falls back to base.GetDesiredSize(), which returns the full constraint height, producing an incorrect measured value. ### Fix Description: The fix involves performing a temporary in-place layout pass during pre-mount measurement. If Window == null, the CollectionView frame is set directly to the given constraints (clamping ∞ to 10000 to match UIKit’s UILayoutFittingExpandedSize). Then SetNeedsLayout() and LayoutIfNeeded() are called so UIKit computes the real content size. The original frame is always restored in a finally block. No AddSubview, RemoveFromSuperview, or ReloadData() is used. This allows Measure() to return the correct height even before the control is mounted. **Reason for Mac behavior:** On macOS via MacCatalyst, UISheetPresentationController always presents sheets as full-height modal panels. Custom detents (custom heights) are ignored by the platform. ### Issues Fixed Fixes #32983 ### Tested the behaviour in the following platforms - [x] iOS - [x] Mac - [ ] Android - [ ] Windows ### Output Screenshot Before Issue Fix | After Issue Fix | |----------|----------| |<video width="100" height="100" alt="Before Fix" src="https://github.com/user-attachments/assets/8f31b5fe-6482-490c-b6f4-c77376257042">|<video width="100" height="100" alt="After Fix" src="https://github.com/user-attachments/assets/d0b2e698-8843-4d9b-a773-0bd3db427b7c">|
…t tab becomes invisible (#34372) <!-- 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. !!!!!!! --> ### Issue Details Setting IsVisible = false on the first tab's page in a Shell with 7 tabs causes the bottom tab bar to display incorrect tabs on Android and breaks sub-page navigation on iOS. - On Android, the tab bar is not rebuilt — Tab1 remains visible and Tab5 ends up highlighted at the wrong index (Tab4 appears selected instead of Tab5). - On iOS, navigating to a sub-page from Tab5 after Tab1 is hidden pushes onto MoreNavigationController instead of the tab's own navigation stack, causing the page to not display. - Both bugs are triggered by _tab1Page.IsVisible = false followed by Shell.Current.GoToAsync("///Tab5"). ### Root Cause **Android**: SetupMenu() has an early return if (DisplayedPage is null). When Tab1 becomes invisible, Shell transitions CurrentItem to a lazy-loaded tab (ContentTemplate), making DisplayedPage transiently null. SetupMenu() returns early, leaving BottomNavigationView stale with Tab1 still in the menu. When GoToAsync("///Tab5") fires, GetItems().IndexOf(Tab5) returns 3 (Tab1 excluded), but menu.GetItem(3) points to Tab4 in the stale menu — wrong tab is highlighted. **iOS**: After Tab1 is removed, IsInMoreTab is never recalculated. Tab5 was at index 4 (IsInMoreTab = true) before removal; after it shifts to index 3 (a direct tab), its renderer still holds IsInMoreTab = true, causing GoToAsync("Page51") to be routed through MoreNavigationController where it fails to display. ### Description of Change **Android**: Changed SetupMenu() guard from if (DisplayedPage is null) → if (DisplayedPage is null && !_menuSetup). Allows rebuild during tab transitions when menu was already initialized, first-time setup guard unchanged. **iOS**: Added UpdateIsInMoreTabForRenderers() after tab removal in OnShellItemsChanged. Recalculates IsInMoreTab for all remaining view controllers based on their new position relative to the 5-tab limit. Validated the behavior in the following platforms - [x] Android - [x] Windows - [x] iOS - [x] Mac ### Issues Fixed Fixes #34343 ### Output ScreenShot Android |Before|After| |--|--| | <video src="https://github.com/user-attachments/assets/6ec28df8-47f2-490b-981b-170fc4d7dea9" >| <video src="https://github.com/user-attachments/assets/91f2faf3-6153-4951-975c-e25c19fa4ef3">| iOS |Before|After| |--|--| | <video src="https://github.com/user-attachments/assets/2b632528-082e-4eb1-8d3a-09871c78c903" >| <video src="https://github.com/user-attachments/assets/8122082c-80a1-4408-a51e-0069244f0b69">|
…erView item holders (#34452) <!-- 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: The issue occurs because of RecyclerView reusing stale ViewHolders from its shared recycled-view pool when CollectionView.EmptyView is swapped on Android. When EmptyView or EmptyViewTemplate is updated, EmptyViewAdapter._emptyItemViewType changes. However, the RecyclerView recycled-view pool may still contain ViewHolders created for previous item view types. As a result, RecyclerView can reuse one of these stale holders at the empty position, causing a previously filtered data item view to be rendered instead of the intended EmptyView. ### Fix Description: The fix involves clearing the RecyclerView recycled-view pool during the EmptyView update path. Specifically, GetRecycledViewPool().Clear() is added in MauiRecyclerView.UpdateEmptyView() and executed only when the currently attached adapter is _emptyViewAdapter (GetAdapter() == _emptyViewAdapter). This ensures that any stale ViewHolders are removed before _emptyViewAdapter.NotifyDataSetChanged() refreshes the view, preventing RecyclerView from reusing incorrect holders while keeping the existing adapter behavior unchanged. ### Issues Fixed Fixes #34122 ### Tested the behaviour in the following platforms - [x] iOS - [x] Mac - [x] Android - [x] Windows **Note:** ### Output Screenshot Before Issue Fix | After Issue Fix | |----------|----------| |<video width="100" height="100" alt="Before Fix" src="https://github.com/user-attachments/assets/f0dda99c-9eff-4078-8b87-72822fa52e13">|<video width="100" height="100" alt="After Fix" src="https://github.com/user-attachments/assets/ac702989-048c-440d-9f3c-a767b0f6eb4f">| --------- Co-authored-by: Shane Neuville <5375137+PureWeen@users.noreply.github.com> Co-authored-by: Jakub Florkowski <kubaflo123@gmail.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…sing scroll/rendering issues (#27093) ### Description of Change Vertical padding set on the recyclerView affected the rendering process. It is much more efficient to fully rely on ItemDecoration to manage spacing between items. ### Issues Fixed Fixes #24511 Fixes #8422 Fixes #18367 Fixes #17127 Fixes #30979 Fixes #31966 |Before|After| |--|--| <video src="https://github.com/user-attachments/assets/e19124a4-c0ba-4796-9906-f6179fa77ba1" width="300px"/>|<video src="https://github.com/user-attachments/assets/f60fee7b-41dc-4b08-a7a0-a464c1a897dc" width="300px"/>| <video src="https://github.com/user-attachments/assets/3827b6fc-bed2-4a80-8422-2ea7602e9970" width="300px"/>|<video src="https://github.com/user-attachments/assets/55daaee6-f198-40d9-b20a-fbb185001954" width="300px"/>| Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ews when SearchBoxVisibility changes at runtime (#33774) <!-- 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 When `SearchBoxVisibility` is `Collapsible`, `ShellToolbarTracker.UpdateToolbarItems` adds a placeholder menu item (id `_placeholderMenuItemId`) to the toolbar menu and sets the `_searchView.View` as that item's action view. When transitioning to `Expanded`, the old code only checked whether `_searchView.View.Parent != _platformToolbar` and added the view directly to the toolbar — but never removed the placeholder menu item from the menu. As a result, both the menu item (showing the collapsible search icon) and the directly-added expanded search view were displayed simultaneously. The reverse transition (Expanded → Collapsible) did not have this problem because the Collapsible path always calls `menu.RemoveItem(_placeholderMenuItemId)` before re-adding it fresh. ### Description of Change In `ShellToolbarTracker.cs`, the `Expanded` branch of `UpdateToolbarItems` now explicitly removes the placeholder menu item from the toolbar menu before adding `_searchView.View` directly to `_platformToolbar`: ```csharp else if (SearchHandler.SearchBoxVisibility == SearchBoxVisibility.Expanded) { // Remove the placeholder menu item, if it exists, added for collapsible mode. if (menu.FindItem(_placeholderMenuItemId) is not null) { menu.RemoveItem(_placeholderMenuItemId); } if (_searchView.View.Parent != _platformToolbar) { _platformToolbar.AddView(_searchView.View); } } ``` Several null-check expressions were also modernized from `!= null` / `== null` to the C# `is not null` / `is null` pattern as part of the same edit. ### Files Changed - **`ShellToolbarTracker.cs`** — Core fix: remove placeholder menu item in the Expanded branch; style cleanup (null-checks, brace style) - **`TestCases.HostApp/Issues/Issue33772.cs`** — New test page: Shell with buttons to toggle `SearchBoxVisibility` between Collapsible and Expanded - **`TestCases.Shared.Tests/Tests/Issues/Issue33772.cs`** — Two ordered UI tests validating the transition in both directions via screenshot - **Snapshot PNGs** — Baseline screenshots for Android, iOS, and Windows ### Issues Fixed Fixes #33772 ### Platforms Tested - [x] Android - [x] iOS - [ ] Mac - [ ] Windows </details> ### Screenshot | Before Fix | After Fix | |----------|----------| | <video src="https://github.com/user-attachments/assets/716652d6-7f39-4c64-8fa9-262dd8cb2908"> | <video src="https://github.com/user-attachments/assets/403990ac-d6fd-4b8c-8114-ff1cdf955df3"> |
…et to null (#31340) <!-- 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 - The BackgroundColor and Background of a ContentView are not properly cleared when set to null on Android and iOS platforms. ### Root Cause - The existing platform-specific logic on both Android and iOS only handled background clearing for specific view types, such as LayoutViewGroup or LayoutView. ContentView was not included in these type checks. As a result, if a ContentView previously had a background applied—such as a color or gradient—and was later set to null, the background remained visible. This occurred because the rendering logic did not execute the background-clearing code for ContentView, leading to incorrect visual behavior. ### Description of Change - Updated UpdateBackground extension methods on Android and iOS to ensure that setting the background or background color to null correctly clears the background for both LayoutView and ContentView types. ### Issues Fixed Fixes #18933 ### Validated the behaviour in the following platforms - [x] Windows - [x] Android - [x] iOS - [x] Mac ### Output | Platform | Before | After | |----------|----------|----------| | Android | <video src="https://github.com/user-attachments/assets/5d3ac9aa-3dbe-4841-9847-6b7a1484e141"> | <video src="https://github.com/user-attachments/assets/5ccb6afb-8d3e-420b-8255-e0325fe6e6ad"> | | iOS | <video src="https://github.com/user-attachments/assets/9d7659f5-3409-4677-862e-fb0ee1853128"> | <video src="https://github.com/user-attachments/assets/8851de12-bdc5-4633-aaec-9bf2be940450"> |
… between flyout items (#29883) ### Root Cause: - On Android, the BottomNavigationView initially has a ColorDrawable with a white background, which is the default color during its creation. - Later, the BottomNavigationView background is changed to a ColorChangeRevealDrawable with an animation, transitioning to the color specified by IShellAppearanceElement.EffectiveTabBarBackgroundColor. - Since the previous background color (white) differs from the current color (black), the animation displays a transition from white to black when switching pages. ### Description of Change: - To resolve this issue, the IShellAppearanceElement.EffectiveTabBarBackgroundColor is applied to the background of the BottomNavigationView immediately after its creation. - This ensures that no animation is triggered later, as the previous background color (black) and the current color (black) remain the same. **Tested the behaviour in the following platforms.** - [x] Android - [x] Windows - [x] iOS - [x] Mac ### Reference N/A ### Issues Fixed Fixes #16522 ### Screenshots | Before | After | |--------|-------| | <img src="https://github.com/user-attachments/assets/3e14b05f-70dd-4a17-bd85-01bf53f37095" width="300" height="600"> | <img src="https://github.com/user-attachments/assets/5320a886-ba23-4142-b4fb-6d00a53f3323" width="300" height="600"> |
…nce customization (#27848) ### Issues Fixed Fixes #27846 Fixes #26975 |Before|After| |--|--| |<video src="https://github.com/user-attachments/assets/d7215924-10c5-487d-9e00-d170aab4715c" width="300px"/>|<video src="https://github.com/user-attachments/assets/ccd8f6ac-f305-4f3b-b423-e9fb9285bce4" width="300px"/>|
…th nullable types (#33429) > [!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! ## Description Fixes #33420 This PR fixes a that occurs when using with nullable types during Shell navigation. ## Root Cause In `ShellContent.ApplyQueryAttributes`, the code used `Convert.ChangeType(value, prop.PropertyType)` which fails when the property type is nullable (e.g., `long?`, `int?`) because `Convert.ChangeType` does not handle `Nullable<T>` types directly. When a user navigates with a nullable parameter like: ```csharp Dictionary<string, object> param = new() { { nameof(DetailsPage.ID), (long?)1 } }; await Shell.Current.GoToAsync(nameof(DetailsPage), param); ``` And the destination page has: ```csharp [QueryProperty(nameof(ID), nameof(ID))] public partial class DetailsPage : ContentPage { public long? ID { get; set; } } ``` The app would crash with `InvalidCastException`. ## Solution Use `Nullable.GetUnderlyingType()` to extract the underlying type before conversion: 1. Check if the property type is nullable using `Nullable.GetUnderlyingType()` 2. If nullable, extract the underlying type (e.g., `long` from `long?`) 3. Convert the value to the underlying type 4. Assign (automatic wrapping in nullable occurs) 5. Handle null values explicitly ## Changes **Fix:** - `src/Controls/src/Core/Shell/ShellContent.cs` - Added nullable type handling in `ApplyQueryAttributes` **Tests:** - Added UI test `Issue33420` that reproduces the issue and verifies the fix - Test navigates with a nullable `long?` parameter and verifies navigation succeeds ## Testing ✅ Tests fail without the fix (reproduces the crash) ✅ Tests pass with the fix (navigation works) ✅ Verified on iOS simulator ## Platforms Affected All platforms (iOS, Android, Windows, MacCatalyst) - Shell navigation code is shared. ---------
Member
|
/azp run maui-pr-uitests, maui-pr-devicetests |
|
Azure Pipelines successfully started running 2 pipeline(s). |
…derer merge - Remove iOSLargeTitlePage.xaml/xaml.cs (main converted to C#-only) - Properly 3-way merge ShellItemRenderer.cs: keep main's ViewDidAppear + net11.0's badge support, fix missing closing braces - Take main's XmlName.cs (adds xCode) + net11.0's xShared - Combine AnalyzerReleases with renumbered MAUIX2015 → MAUIX2017 Validated with dotnet cake - 0 errors. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Contributor
|
/azp run maui-pr-uitests, maui-pr-devicetests |
|
Azure Pipelines successfully started running 2 pipeline(s). |
This was referenced Apr 17, 2026
Open
Open
…34804) ## Problem Android binding builds invoke Gradle to resolve Maven dependencies (e.g., `androidx` packages). When Gradle downloads fail due to transient DNS/network issues on CI agents, the build fails with: ``` gradlew exited with code 1 ``` This is intermittent and often passes on retrigger, but wastes CI capacity and blocks PRs. See #34800 for recent examples. ## Fix Add a shared `cache-gradle.yml` template using the AzDO [`Cache@2`](https://learn.microsoft.com/azure/devops/pipelines/release/caching) task to persist `~/.gradle/caches` between pipeline runs. When the cache is warm, Gradle resolves dependencies locally instead of downloading from Maven Central, eliminating transient network failures. The cache key includes: - `Agent.OS` — separate caches for macOS/Windows/Linux - `gradle-wrapper.properties` — invalidates when Gradle version changes - `build.gradle` files — invalidates when dependencies change The template is called from `provision.yml` so it applies to all pipelines (build, pack, device tests, UI tests). ## Prior art Adapted from the same approach in dotnet/android: dotnet/android@0a68102 (PR #10986) Fixes #34800 --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Member
|
/azp run maui-pr-uitests, maui-pr-devicetests |
|
Azure Pipelines successfully started running 2 pipeline(s). |
…ap definition - Update SourceGen and XAML unit tests to reference MAUIX2017 (was MAUIX2015, renumbered to avoid collision with main's XCodeNotChildOfRoot) - Align TestCategory.Map to use nameof(Map) matching net11.0 to avoid duplicate definition when GitHub merges PR branch with net11.0 Validated with dotnet cake - 0 errors. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Member
|
/azp run maui-pr-uitests, maui-pr-devicetests |
|
Azure Pipelines successfully started running 2 pipeline(s). |
…on merge Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This was referenced Apr 21, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
I detected changes in the main branch which have not been merged yet to net11.0. I'm a robot and am configured to help you automatically keep net11.0 up to date, so I've opened this PR.
This PR merges commits made on main by the following committers:
Instructions for merging from UI
This PR will not be auto-merged. When pull request checks pass, complete this PR by creating a merge commit, not a squash or rebase commit.
If this repo does not allow creating merge commits from the GitHub UI, use command line instructions.
Instructions for merging via command line
Run these commands to merge this pull request from the command line.
or if you are using SSH
After PR checks are complete push the branch
Instructions for resolving conflicts
Instructions for updating this pull request
Contributors to this repo have permission update this pull request by pushing to the branch 'merge/main-to-net11.0'. This can be done to resolve conflicts or make other changes to this pull request before it is merged.
The provided examples assume that the remote is named 'origin'. If you have a different remote name, please replace 'origin' with the name of your remote.
or if you are using SSH
Contact .NET Core Engineering (dotnet/dnceng) if you have questions or issues.
Also, if this PR was generated incorrectly, help us fix it. See https://github.com/dotnet/arcade/blob/main/.github/workflows/scripts/inter-branch-merge.ps1.