Skip to content

[iOS] Restore large-title scrolling for nested compatibility lists - #39502

Draft
jfversluis wants to merge 181 commits into
inflight/currentfrom
fix-33037-minimal
Draft

jfversluis wants to merge 181 commits into
inflight/currentfrom
fix-33037-minimal

Conversation

@jfversluis

Copy link
Copy Markdown
Member

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!

Description of Change

Smaller servicing replacement for #38067, following kubaflo's review. This deliberately retains the original PR's inflight/current target. No new public APIs, API-file changes, or changes to the Core assembly.

On iOS 26, a compatibility ListView/TableView inside a non-Shell NavigationPage layout can remain below the large navigation bar without participating in UIKit's large-title scrolling. Its table controller can also lack page-controller containment, preventing native safe-area inheritance.

For a single eligible compatibility list, arrange its branch beneath the translucent bar in one managed layout pass, establish missing table-controller containment, and register the native scroll view for UIKit's top inset handling. Keep sibling overlays and footers in their ordinary safe bounds. Restore the previous native registration/inset behavior and release owned containment when the host is removed, reparented, or disconnected. Also remeasure the large-title bar after its navigation controller changes size while absent, covering rotation beneath a full-screen cover.

Scope is intentionally narrow: iOS 26+, non-Catalyst, root ContentPage layouts with implicit Container top safe area, a visible translucent large-title compatibility NavigationPage, and exactly one supported ListView/TableView. Explicit safe areas, fixed headers, multiple lists, top margins/padding, fixed/non-fill list sizing, opaque/hidden bars, and Never titles are excluded. CollectionView and ScrollView behavior is not changed. This is not a general safe-area redesign.

Native-only post-layout frame changes and two-pass safe/edge-to-edge arrangement were discarded: compatibility renderers restore managed bounds, and the intermediate safe layout can clamp the scroll offset and break title restoration.

Validation

On an iPhone 17 Pro simulator running iOS 26.5:

  • HEADLESS=true pwsh .github/scripts/BuildAndRunHostApp.ps1 -Platform ios -DeviceUdid 7B88EF5A-5797-465A-A193-EB0ABF44F55C -TestFilter 'FullyQualifiedName~Issue33037NonShell' — 15 passed. Covers reporter/nested/table collapse and restoration, short reverse gestures, stable list geometry, expanded/collapsed cover+rotation re-entry, excluded layouts, dynamic explicit-safe-area ownership, and live list transfer from a cached page.
  • HEADLESS=true DEVICE_UDID=7B88EF5A-5797-465A-A193-EB0ABF44F55C pwsh .github/skills/verify-tests-fail-without-fix/scripts/verify-tests-fail.ps1 -Platform ios -TestType UITest -PRNumber local-33037 -BaseBranch be754b5e9a1e242d54f159d2537778b7c185e51d -TestFilter 'FullyQualifiedName~Issue33037NonShell.LargeTitleCollapsesAndRestoresWithoutMovingTheList' -RequireFullVerification — VERIFICATION PASSED: all three cases fail on the frozen baseline (title stays 42 points instead of collapsing below 30), and all three pass with the fix.
  • dotnet test src/Controls/tests/Core.UnitTests/Controls.Core.UnitTests.csproj --filter 'FullyQualifiedName~LayoutTests' --no-restore — 394 passed.
  • dotnet build src/Controls/src/Core/Controls.Core.csproj -f net10.0-maccatalyst26.0 -p:ValidateXcodeVersion=false --no-restore — succeeded, 0 warnings/errors.

Local iOS runs additionally exported XCODE_XCCONFIG_FILE pointing to a local IPHONEOS_DEPLOYMENT_TARGET = 15.0 override and CustomAfterMicrosoftCommonTargets pointing to a local target adding LinkerArgument values -rpath and /usr/lib/swift. These compensate for the installed Xcode 27.1/.NET iOS SDK combination; neither override is part of this PR. An earlier A/B attempt was blocked by this toolchain's deployment-target/Swift-runtime startup errors, not treated as reproduction evidence. The final runs above executed the tests successfully. The runner also emits a separate device-log collection warning after successful test completion.

Independent source reviewers identified and helped resolve ownership/teardown issues; a separate runtime reviewer exercised nested compact/expanded cover+rotation re-entry and restoration. All final consultations reported no blocking findings. This is bounded regression evidence, not a guarantee for untested configurations; older iOS versions, physical devices, Android, and Windows were not functionally run. Runtime screenshots and a short recording will be attached in a comment.

Issues Fixed

Fixes #33037

Supersedes #38067 for the remaining non-Shell compatibility-list scenario.

dependabot Bot and others added 30 commits October 19, 2026 01:06
Updated
[Microsoft.AspNetCore.Authentication.Facebook](https://github.com/dotnet/dotnet)
from 10.0.0 to 10.0.10.

<details>
<summary>Release notes</summary>

_Sourced from [Microsoft.AspNetCore.Authentication.Facebook's
releases](https://github.com/dotnet/dotnet/releases)._

No release notes found for this version range.

Commits viewable in [compare
view](https://github.com/dotnet/dotnet/commits).
</details>

Updated
[Microsoft.AspNetCore.Authentication.Google](https://github.com/dotnet/dotnet)
from 10.0.0 to 10.0.10.

<details>
<summary>Release notes</summary>

_Sourced from [Microsoft.AspNetCore.Authentication.Google's
releases](https://github.com/dotnet/dotnet/releases)._

No release notes found for this version range.

Commits viewable in [compare
view](https://github.com/dotnet/dotnet/commits).
</details>

Updated
[Microsoft.AspNetCore.Authentication.MicrosoftAccount](https://github.com/dotnet/dotnet)
from 10.0.0 to 10.0.10.

<details>
<summary>Release notes</summary>

_Sourced from [Microsoft.AspNetCore.Authentication.MicrosoftAccount's
releases](https://github.com/dotnet/dotnet/releases)._

No release notes found for this version range.

Commits viewable in [compare
view](https://github.com/dotnet/dotnet/commits).
</details>

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore <dependency name> major version` will close this
group update PR and stop Dependabot creating any more for the specific
dependency's major version (unless you unignore this specific
dependency's major version or upgrade to it yourself)
- `@dependabot ignore <dependency name> minor version` will close this
group update PR and stop Dependabot creating any more for the specific
dependency's minor version (unless you unignore this specific
dependency's minor version or upgrade to it yourself)
- `@dependabot ignore <dependency name>` will close this group update PR
and stop Dependabot creating any more for the specific dependency
(unless you unignore this specific dependency or upgrade to it yourself)
- `@dependabot unignore <dependency name>` will remove all of the ignore
conditions of the specified dependency
- `@dependabot unignore <dependency name> <ignore condition>` will
remove the ignore condition of the specified dependency and ignore
conditions


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@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!

### Description of Change

Removes the private `DropCompletedEventArgs.DropResult` property. The
property was never assigned or consumed and always held its default
value. `DropCompletedEventArgs` remains useful through its public
`PlatformArgs` property.

This does not change the public API surface.

### Issues Fixed

Fixes #2839
…ating project cards (#37385)

<!-- 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 voice-over does not announce the project description when the focus
moves to the border element within the item template of the
CollectionView on the Mac platform.

### Root Cause 

SemanticProperties.Description was bound only to the project Name on the
parent Border. Because the card is exposed as one accessible/selectable
element, VoiceOver does not announce the child labels separately

### Description of Change
 
Combined both Name and Description values into the Border semantic
description.
 
### Issues Fixed
 
Fixes #37142 
 
**Tested the behavior in the following platforms.**
- [x] Android
- [x] Windows
- [x] iOS
- [x] Mac
 
| Before  | After  |
|---------|--------|
| <video
src="https://github.com/user-attachments/assets/20884ac2-7f4c-4907-8d97-02326a093be2"
Width="600" Height="400"/>| <video
src="https://github.com/user-attachments/assets/c0f63c4e-4e12-4af4-976d-439970db7a67"
Width="600" Height="400"/> |
…ist item (#37340)

<!-- 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
Issue Details
When a VisualStateGroup is replaced through the VisualStateGroupList
indexer, the existing implementation only updates the internal list:

`visualStateGroups[index] = replacementGroup;`

The replacement bypasses the lifecycle behavior used by Add, Insert, and
Remove:
* The old group remains subscribed to StatesChanged.
* The replacement group is not subscribed to StatesChanged.
* Group and state names are not revalidated.
* State triggers are not updated for the replacement.
* The associated VisualElement is not notified to reevaluate its visual
state.

### Description of Change

<!-- Enter description of the fix in this section -->
* Updated the VisualStateGroupList indexer setter to correctly manage
group replacement:
* Reject null replacement values with ArgumentNullException.
* Unsubscribe ValidateAndNotify from the replaced group.
* Replace the group in the internal collection.
* Subscribe ValidateAndNotify to the new group.
* Explicitly call ValidateAndNotify(_internalList) after replacement.

### 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 #37171 

<!--
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  |
|---------|--------|
| **Android**<br> <img
src="https://github.com/user-attachments/assets/1e6c5337-afcc-4e55-bba2-6852316fdf6a"
width="300" height="600"> | **Android**<br> <img
src="https://github.com/user-attachments/assets/006f5544-3393-4f3a-9774-86ff6d966947"
width="300" height="600"> |
…me instead of item content in Narrator (#37421)

<!-- 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
On Windows, `CollectionView`'s legacy handler (CV1), which is based on
`ListView`, derives each item's screen reader name from the root element
of the `DataTemplate`.

When neither `AutomationProperties.Name` nor
`SemanticProperties.Description` is set on that root element, WinUI
falls back to calling `.ToString()` on the bound item. As a result,
Narrator announces the internal type name:
`Microsoft.Maui.Controls.Platform.ItemTemplateContext` instead of
meaningful item content.

This is native WinUI `ListView` behavior documented by Microsoft and is
not specific to MAUI.

### Description of Change
Set `SemanticProperties.Description` on the `DataTemplate` root,
`TaskView`'s `Border`, and scope it to Windows only. This ensures
Narrator announces the bound task title instead of the internal type
name.

Windows-only scoping is necessary because applying
`SemanticProperties.Description` on iOS and Mac Catalyst causes the root
element to become a single accessibility element, preventing VoiceOver
from navigating to the inner `CheckBox`.

### Issues Fixed

Fixes #36940 

### Platforms Tested

- [x] iOS
- [x] Android  
- [x] Windows
- [x] Mac

### Screenshots
| Before Fix | After Fix |
|------------|-----------|
| <video width="350" alt="withoutfix"
src="https://github.com/user-attachments/assets/b35e24bc-0c59-4db1-8210-1be4fe5c2333"
/> | <video width="350" alt="withfix"
src="https://github.com/user-attachments/assets/89ad2456-5c9d-4060-8685-e108e9fe8c3e"
/> |
<!-- 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

Fixes #29418.

On Windows, `W2DGraphicsView` stored Win2D's callback-scoped
`CanvasDrawingSession` on its persistent canvas. Win2D disposes that
native session after the draw callback returns, but the stale managed
reference remained available across repeated pointer-driven redraws and
shutdown, leading to native access violations and heap corruption.

This change clears the drawing session after canvas state has been
reset. The nested cleanup guarantees the session and thread-local canvas
owner are released even if state reset throws.

@Kamalesh-Periyasamy, once the PR artifacts are available, could you
please try them with your original application and let us know whether
they resolve the crash?

### Tests

- Added a Windows device test that performs an initial draw plus 100
explicit invalidation/draw cycles and verifies the drawing session is
released after every callback.
- Verified the test fails without the fix by observing the retained,
already-disposed `CanvasDrawingSession`.
- Core DeviceTests: 2,092 total, 1,981 passed, 111 skipped, 0 failed.
- Ran the exact issue attachment twice for 120 seconds each with 3,848
presses and 38,480 pointer moves per run; both exited cleanly with code
0 and no correlated Windows application errors.
- Also validated custom/default fonts, `ManipulationMode.All`/`None`,
multiple views, redraw, resize, unload, and graceful shutdown.

### Video verification

#### Before — original reported crash

The issue author's original recording shows the attached app failing
during pointer interaction:


https://github.com/user-attachments/assets/eecd8e89-17c9-4764-b21e-9e6c15141fb4

#### After — fixed DLL under sustained pointer stress

The exact attached application was rebuilt with an ABI-compatible fixed
`Microsoft.Maui.Graphics.Win2D.WinUI.Desktop.dll`, then exercised with
real `SendInput` pointer presses and moves for 120 seconds:


https://github.com/user-attachments/assets/41a3e68c-e048-4d09-b677-aec1a9bc3af7

This recorded run delivered 11,622 presses and 116,220 pointer moves,
then closed normally with exit code `0`. No correlated `Application
Error` or `.NET Runtime` event was recorded.

The native crash is intermittent: fresh recording attempts against the
original DLL did not fail during the capture session, so the before clip
above is the reporter's genuine original failure rather than a clean run
being mislabeled or a crash being staged. The deterministic regression
evidence is the baseline test retaining an already-disposed
`CanvasDrawingSession`; the same test passes after this change.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 01dac3d5-7cea-4af6-ba68-fdc87f9ceb54
…ains prior ShellContent instances (#37390)

<!-- Please keep the note below for people who 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 whether this change resolves your
issue. Thank you!

### Description of Change

**Memory management and event subscription improvements:**

* Adds a destructor (`~ShellContent`) to `ShellContent` that
unsubscribes from property change notifications, preventing memory leaks
when a shared `Page` is assigned to multiple transient `ShellContent`
instances (`src/Controls/src/Core/Shell/ShellContent.cs`).
* Refactored event subscription logic to use
`WeakNotifyPropertyChangedProxy` and a cached handler, ensuring that
event handlers do not prevent garbage collection of `ShellContent`
instances (`src/Controls/src/Core/Shell/ShellContent.cs`).

**Testing and regression coverage:**

* Adds a unit test to verify that assigning the same long-lived `Page`
to many transient `ShellContent` instances does not retain those
instances in memory, addressing issue #37217
(`src/Controls/tests/Core.UnitTests/ShellTests.cs`).

**Public API updates:**

* Adds destructor signature for `ShellContent` to the platform-specific
`PublicAPI.Unshipped.txt` files to document the new public API surface
(`src/Controls/src/Core/PublicAPI/net-android/PublicAPI.Unshipped.txt`,
`src/Controls/src/Core/PublicAPI/net-ios/PublicAPI.Unshipped.txt`,
`src/Controls/src/Core/PublicAPI/net-maccatalyst/PublicAPI.Unshipped.txt`,
`src/Controls/src/Core/PublicAPI/net-tizen/PublicAPI.Unshipped.txt`,
`src/Controls/src/Core/PublicAPI/net/PublicAPI.Unshipped.txt`,
`src/Controls/src/Core/PublicAPI/netstandard/PublicAPI.Unshipped.txt`,
`src/Controls/src/Core/PublicAPI/net-windows/PublicAPI.Unshipped.txt`).
[[1]](diffhunk://#diff-4731d7bdf78f0e9d2a7a64d6f56502e3d2095313d039e7f09c0e7d1552471f8eR25)
[[2]](diffhunk://#diff-793476570313a2965972f9be2c01f3eaaf5eec16dfab1b6b7e34cd5473af4277R18)
[[3]](diffhunk://#diff-b98fb401b945e281e04d9aabd0695677c639da1423ae935f1e521e88e8cae021R8)
[[4]](diffhunk://#diff-923a9c83577000b182ef448083ba67a73bd622126eafbd11d265afe0e2387a68R10)

<!-- 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 #37217 

### Tested the behavior in the following platforms

- [x] Windows
- [x] Android
- [x] iOS
- [x] Mac

| Before Issue Fix | After Issue Fix |
|---|---|
| <video
src="https://github.com/user-attachments/assets/c8166be4-b3b0-4706-8a38-ebfc6778f6ff">
| <video
src="https://github.com/user-attachments/assets/c26f1915-2152-4c5e-b923-2659a5b13f3a">
|

<!--
Are you targeting main? All PRs should target the main branch unless
otherwise noted.
-->
… to its condition by configuration/target (#37176)

<!-- 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

MauiXaml will generate source for every xaml file without respect to its
condition by configuration/target

### Root Cause 

• Microsoft.Maui.Controls.Globs.props globs **\*.xaml into the MauiXaml
item group unconditionally for every inner build/TFM.
• Microsoft.Maui.Controls.SingleProject.targets correctly gates Compile
( .cs ) items under Platforms/<Platform>/ via
%(Compile.ExcludeFromCurrentConfiguration) , but its XAML-removal logic
only ever stripped Windows .xaml (WinUI markup) out of MauiXaml . It
never removed non-active-platform .xaml under Platforms/Android ,
Platforms/iOS , Platforms/MacCatalyst , or Platforms/Tizen .
• Consequence: the XAML SourceGen consumes MauiXaml as AdditionalFiles
regardless of platform, so every platform's xaml leaked into every inner
build — producing duplicate InitializeComponent /attribute/field
declarations whenever multiple platforms reused the same class name (a
standard, supported pattern).

### Description of Change
 

src/Controls/src/Build.Tasks/nuget/buildTransitive/netstandard2.0/Microsoft.Maui.Controls.SingleProject.targets
(shipped via buildTransitive in Microsoft.Maui.Controls.Build.Tasks →
applies to every MAUI app)
 
Extended the _MauiRemovePlatformCompileItems target's _MauiXamlToRemove
computation (previously Windows-only) to also cover
Android/iOS/MacCatalyst/Tizen: for each built-in platform folder, its
.xaml files are queued for removal from MauiXaml / EmbeddedResource
unless it matches the currently active $(TargetPlatformIdentifier) . The
pre-existing unconditional Windows-folder removal (WinUI markup, not
MAUI XAML) is left unchanged.

 
### Issues Fixed
 
Fixes #36951 
 
**Tested the behavior in the following platforms.**
- [x] Android
- [x] Windows
- [x] iOS
- [x] Mac
 
| Before  | After  |
|---------|--------|
| <img
src="https://github.com/user-attachments/assets/a0fd36d9-2094-4948-a645-c3c0d1bf0e4e"
Width="600" Height="300" /> | <img width="600" height="300" alt="image"
src="https://github.com/user-attachments/assets/510d9024-dacc-4447-821d-29e7129ed844"
/> |
### Description of Change

Captures the weak-reference fields in local variables before testing and
dereferencing them. This closes the time-of-check/time-of-use race with
`Unsubscribe()`, which clears the fields concurrently.

The race has been observed as an unhandled Android
`NullReferenceException` from both `TryGetSource` during binding
finalization and `TryGetHandler` while processing property changes.

### Issues Fixed

Fixes #32602

### Validation

- `dotnet build src/Controls/src/Core/Controls.Core.csproj --no-restore
--verbosity minimal -p:TargetFramework=netstandard2.0`

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
## Description

Keeps `VisualElement.IsVisible` as the locally assigned/bound value
while making handler-facing `IView.Visibility` reflect cumulative
visibility through the visual ancestor chain.

Effective visibility changes are cached and propagated through the
existing property-propagation pipeline. Unchanged branches are pruned,
so descendant handlers and measure invalidation run only for actual
effective visibility transitions. Reparenting and detaching are covered.

Fixes #19139.

## Regression tests

- Hidden and nested ancestors
- Ancestor visibility changes without mutating child `IsVisible`
- Reparenting and detaching
- Non-layout visual children
- Handler mapping and unchanged-branch pruning
- Measure invalidation

## Validation

- `VisualElementTests`: 29 passed
- `Issue1549` XAML compatibility tests: 3 passed
- iOS runtime: `IsVisible` remains `True`, `IView.Visibility` is
`Collapsed`, and keyboard Next skips the hidden entry
- Android runtime: `IsVisible` remains `True`, `IView.Visibility` is
`Collapsed`, and IME Next skips the hidden entry

---------
…eak Device Test (#37310)

<!-- 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

Device memory leak tests started failing in the Candidate branch after
PR #35487, indicating regressions in
memory leak validation across Windows, iOS, macOS, and Android. The
affected Does Not Leak and Window Does Not Leak tests were failing on
these platforms.

### Issue1: Window Does Not Leak 

### Root cause

The WindowDoesNotLeak test failed intermittently because window and page
were declared as locals inside an async Task method ( WindowDoesNotLeak
) and were referenced across await boundaries (both directly and inside
the lambda passed to CreateHandlerAndAddToWindow ). The C# compiler
hoists such locals into fields of the generated async state machine, and
those fields are not guaranteed to be cleared once the locals are no
longer needed. As a result, window and page remained artificially rooted
by the state machine instance for the entire duration of the subsequent
WaitForGC call, producing a false-positive "still alive" leak report
rather than reflecting an actual product defect.

### Solution description

The fix extracts the window/page creation and handler invocation logic
out of WindowDoesNotLeak into a new, non-inlineable helper method,
CreateWindowLeakReferencesAsync , marked with
[MethodImpl(MethodImplOptions.NoInlining)] . This helper creates window
and page , drives the handler lifecycle, and returns only the collected
WeakReference list. Since window and page now live in the helper's own
async state machine — which is discarded once the helper returns — they
are no longer reachable from WindowDoesNotLeak 's state machine during
the subsequent WaitForGC call, eliminating the artificial rooting. The
dead conditional compilation guard ( #if TEST_FAILS_ON_ANDROID &&
TESTS_FAILS_ON_WINDOWS && TESTS_FAILS_ON_IOS &&
TESTS_FAILS_ON_MACCATALYST ) that had disabled the test was also removed
to re-enable it.

### Issue2: TabbedPageTests.DoesNotLeak

### Root cause

The issue occurs because of two compounding factors. At the test level,
the navPage local was captured by an async lambda and accessed again
after an await , causing it to be hoisted into the compiler-generated
closure and transitively keep the popped TabbedPage alive through its
navigation stack. At the platform level on Android,
MultiPageFragmentStateAdapter was constructed using the host Activity's
long-lived Lifecycle , causing AndroidX to permanently register a
lifecycle observer that was never released because the Activity's
lifecycle never reached the destroyed state. This kept the adapter, and
the TabbedPage it referenced, permanently rooted in memory, while
AndroidX also failed to reclaim the associated child fragments.

### Solution description

The fix involves nulling the navPage local before the WaitForGC call at
the test level, combined with a production-level Android fix in
MultiPageFragmentStateAdapter.cs that introduces a teardown routine to
make AndroidX reclaim its fragments correctly and explicitly clears the
adapter's page reference on disposal. Supporting changes in
TabbedPageManager.cs and TabbedPage.Android.cs ensure the teardown
routine executes before the adapter is detached and that manager
references are cleared on disconnect. The resolution was verified
through repeated on-device Android test runs, all passing successfully.

### Issue3: NavigationPageTests.DoesNotLeak

### Root Cause
 
The issue occurs because of the `navPage` and `window` locals in
`NavigationPageTests.DoesNotLeak` being referenced across an `await`
boundary — both directly and inside the lambda passed to
`CreateHandlerAndAddToWindow`. The C# compiler hoists such locals into
fields of the generated async state machine for the test method, and
those fields are not guaranteed to be cleared once the locals are no
longer needed. As a result, `navPage` and `window` remained artificially
rooted by the state machine instance for the duration of the subsequent
`WaitForGC` call, producing a false-positive "still alive" leak report
rather than reflecting an actual product defect.
 
This issue was only replicated in Release mode, since Release-mode
JIT/compiler optimizations affect how and when the hoisted state-machine
fields are cleared, whereas Debug builds happened to clear or avoid
rooting them, masking the problem locally.
 
### Solution Description
 
The fix involves extracting the `navPage`/`window` creation and handler
invocation logic out of `DoesNotLeak` into a new, non-inlineable helper
method, `CreateNavigationPageLeakReferencesAsync`, marked with
`[MethodImpl(MethodImplOptions.NoInlining)]`. This helper creates
`navPage` and `window`, drives the handler lifecycle, and returns only
the collected `WeakReference` list.
 
Since `navPage` and `window` now live in the helper's own async state
machine — which is discarded once the helper returns — they are no
longer reachable from `DoesNotLeak`'s state machine during the
subsequent `WaitForGC` call, eliminating the artificial rooting.

Validated the behavior in the following platforms
 
- [x] iOS
- [x] Mac
- [x] Android
- [x] Windows

### Regression PR

PR #35487
 
### Issues Fixed
  
Fixes #36613

### Output  Screenshot

### Issue1: Window Does Not Leak 

|Platform|Before|After|
|--|--|--|
|Android| <video
src="https://github.com/user-attachments/assets/1d3986aa-5c5f-4d97-ab6e-96c586e53488"
>| <video
src="https://github.com/user-attachments/assets/3670bafa-6a75-47a8-8505-797901e7abeb">|
|Mac|<video
src="https://github.com/user-attachments/assets/461534f8-21dd-46ab-bb62-c10a5f8ac71a">
|<video
src="https://github.com/user-attachments/assets/d862eb7f-c33f-4866-a553-5264b1cda411">|
|iOS|<video
src="https://github.com/user-attachments/assets/41b71c49-26ac-4dcc-9e01-87dce42d5000">|<video
src="https://github.com/user-attachments/assets/9e03f7ca-d803-49b9-a159-4057320591a7">|
|Windows|<video
src="https://github.com/user-attachments/assets/73300a73-a9be-403b-b77e-2cc4418d10c9">|<video
src="https://github.com/user-attachments/assets/173cf2e3-5bde-4383-b07a-0b6b8422d03b">
|

### Issue2: TabbedPageTests.DoesNotLeak

|Platform|Before|After|
|--|--|--|
|Android| <video
src="https://github.com/user-attachments/assets/604316e5-5849-4195-9e16-1c96d710c125"
>| <video
src="https://github.com/user-attachments/assets/50bc614c-814d-43d1-912b-446886086886">|

### Issue3: NavigationPageTests.DoesNotLeak

|Platform|Before|After|
|--|--|--|
|Mac|<video
src="https://github.com/user-attachments/assets/cd6831a8-e8f6-4954-baff-055a2575a576">
|<video
src="https://github.com/user-attachments/assets/6198e780-82e9-4eec-b3f5-a805f79d0ddd">|
|iOS|<video
src="https://github.com/user-attachments/assets/47edefd0-a416-4245-bbce-3e54c80aefa0">|<video
src="https://github.com/user-attachments/assets/84f2c31c-0d9d-4c3e-9f21-9a8f634e671f">|
<!-- 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!

On Windows, propagate `InputTransparent` to a view's `WrapperView` as
well as its inner platform view. This allows pointer input to pass
through wrapped controls, including the default-template `Label` with a
transparent background overlaying a `Button` from #21384.

The fix preserves two important Windows behaviors:

- `ILayoutHandler` continues using `LayoutPanel`'s specialized
background-based input transparency, so transparent layouts do not
disable their interactive child tree.
- Reusing a disconnected handler preserves the existing wrapper and its
visual-tree position while replacing its stale child with the newly
created platform view.

Framework reconnect creates and connects the replacement platform view
unparented before mapper replay. The reconnect repair therefore only
updates `WrapperView.Child`; it does not remove the platform view from
arbitrary parent shapes or re-enter virtual `SetupContainer()` overrides
that could duplicate subscriptions or one-time setup. Normal mapper
replay restores Label/Image sizing and alignment after the child
replacement.

The default template's implicit `Label` style supplies a transparent
background, which causes `LabelHandler` to create a Windows
`WrapperView`. The existing mapper disabled hit testing only on the
inner `TextBlock`; the effective wrapper remained hit-testable and
intercepted pointer input.

- Windows Label device tests: **42 total, 40 passed, 0 failed, 2
skipped**. Coverage includes initial state, runtime toggles, late
wrapper creation, reconnect hit testing and mapped properties, the
framework's unparented replacement-platform-view lifecycle, a wrapper
moved to another attached `Panel`, and a detached wrapper.
- Failure-first reconnect validation: temporarily removing the reconnect
rebinding made all three reconnect-specific tests fail; restoring it
made them pass.
- Windows Layout device tests: **54/54 passed**, covering both
`CascadeInputTransparent` modes and confirming that the wrapper remains
hit-testable for child interaction.
- Windows Core tests: **1,980 passed, 0 failed, 111 skipped**; the exact
CI-reported Slider case and all 90 Slider tests passed locally.
- Windows Appium test and HostApp builds completed with **0 warnings and
0 errors**, and both Issue21384 tests are discovered. The
reporter-faithful Label pointer test is skipped on MacCatalyst because
test remains cross-platform.
- Local Appium execution remains blocked before interaction by
WinAppDriver's invalid status response. The real Win32 `SendInput`
harness below provides behavior-level evidence.

Both recordings use the same `AbsoluteLayout` reproduction from #21384
and send five real Win32 mouse clicks to the overlaid button
(`SendInput`, not UI Automation invoke).

**Before — bug reproduced on parent commit `f377ff1c5e`:** all five
clicks are sent, but the click handler receives none and the visible
counter remains at 0.

https://github.com/user-attachments/assets/d59d3346-355c-434f-b1ac-01c76c361ab1

**After — fixed behavior:** all five equivalent clicks reach the button
and the visible counter advances from 0 to 5. The final production code
was rerun with the same harness and again received **5/5** clicks.

https://github.com/user-attachments/assets/610856db-c094-4657-b4ea-0985b96221dd

- Do not update only the inner Windows platform view; a hit-testable
`WrapperView` can still intercept input.
- Do not disable the wrapper for `ILayoutHandler`; `LayoutPanel`
intentionally preserves child interaction through its background-based
mapper.
- Do not re-enter virtual `SetupContainer()` during reconnect; derived
handlers may repeat subscriptions or one-time side effects.
- Do not add generalized parent-detachment logic for reconnect; the
framework replacement platform view is unparented before mapper replay,
and the surviving wrapper's parent must remain unchanged.

Fixes #21384

@owaits, once the build artifacts are available, could you please try
them with your original reproduction and let us know whether this
resolves the issue for you?

---------

Co-authored-by: Jakub Florkowski <42434498+kubaflo@users.noreply.github.com>
… and callbacks alive (#37639)

<!-- 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

- Disposing `AnimationManager` did not release the manager, active
animations, or callback-captured data when its `ITicker` remained alive.

### Root Cause of the Issue

- `AnimationManager` assigns `OnFire` to `Ticker.Fire` and stores active
animations in `_animations`. Its previous `Dispose()` implementation
only disposed tickers implementing `IDisposable`; it did not remove
`OnFire`, stop a normal `ITicker`, or clear `_animations`. Consequently,
a long-lived ticker could retain the entire object graph.

### Description of Change

<!-- Enter description of the fix in this section -->
### Improvements to resource management and disposal:

* Refactored the `Dispose` method in `AnimationManager` to ensure it is
idempotent, detaches event handlers, clears animations, and disposes of
the ticker only when appropriate. This prevents multiple disposals and
ensures resources are released correctly.

### New unit tests for disposal behavior:

* Added `AnimationManagerTests` with tests to verify:
  - The ticker callback is released without disposing the animation.
- Animation payloads are released when the ticker outlives the manager.
  - The owned callback is properly removed from multicast delegates.
  - Correct disposal and stop counts for the ticker.
  - No unintended side effects on animation callbacks after disposal.

### 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 #37228 

### Tested the behavior in the following platforms
 
- [x] Windows
- [x] Android
- [x] iOS
- [x] Mac

### Output
 
| Before | After |
|----------|----------|
| <img
src="https://github.com/user-attachments/assets/fe7e8c32-eb2c-4808-bc8b-599aa7055344">
| <img
src="https://github.com/user-attachments/assets/83fe5ec1-6462-4942-a461-034f3d25407b">
|



<!--
Are you targeting main? All PRs should target the main branch unless
otherwise noted.
-->
<!-- 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!

Fixes #17523

Windows attached the Border's `CompositionGeometricClip` directly to the
content visual while authoring the geometry in the parent `ContentPanel`
coordinate space. Scale, rotation, and translation therefore transformed
the clip together with the content instead of keeping the Border
aperture stationary.

- Place Border content inside a Border-only stationary native clip host
and attach the geometric clip to that host.
- Route cross-platform measure invalidation to both native layers while
preserving WinUI's cached subtree measure on unrelated parent layout
passes.
- Avoid re-entering measure from arrange when a layout owner changes;
the normal invalidated measure pass remains authoritative.
- Preserve the cross-platform arranged size through the native clip
host, including detached and reconnected layout-owner transitions.
- Cache stable composition and rectangular bounds clip geometry,
rebuilding only when geometry inputs change.
- Tear down layout owners, hosted content, stroke references, and cached
clip state when the handler disconnects, after derived handler
disconnect hooks have observed the intact platform view.
- Safely ignore late invalidation after handler disconnect and
synchronize direct or reconnect-driven layout-owner changes with the
clip host.
- Preserve current cross-parent content reparenting while clearing stale
clip-host ownership when content moves Border → Border, Border → generic
`MauiPanel`, or back again.
- Handle zero-by-infinite WinUI measure constraints without invoking
cross-platform layout.
- Keep layout invalidation flowing for initial asynchronous image loads
and runtime content-size, `FontSize`, and `Padding` changes without
requiring an outer window resize.
- Preserve explicit Border sizing, stroke/shadow behavior, nested layout
clipping, content replacement, custom handler activation and mapper
customization, shape removal, and accessibility behavior.
- Clamp stroke-inset geometry to nonnegative dimensions and preserve an
explicitly empty aperture when the stroke consumes all available content
space, including rounded shapes.
- Clear stale clipping geometry whenever either arranged dimension is
non-positive.
- Remove the obsolete nested `LayoutPanel` clip exception because the
XAML rectangular clip and Border composition clip now live on separate
visuals.
- Update the three affected WinUI Image AspectFill baselines for the
stationary clip host's one-pixel antialiased inner edge; image content
remains pixel-identical.

This is an implementation-only Windows fix; it adds no public APIs.

**Before — issue author's attached reproduction, minimally retargeted to
the current Windows TFM:** transformed image content escapes the
stationary ellipse, rounded rectangle, and triangle apertures as image
scale changes.

https://github.com/user-attachments/assets/7cf6b0fc-c101-44df-ba5b-841131014708

**After — final reviewed PR gallery build using the exact
`BorderResizeContent` source scenario:** the same scale interaction
changes the image while each Border aperture remains stationary and
correctly clips its content.

https://github.com/user-attachments/assets/97a4429a-c32c-4c9f-9252-957b8f2c5e8f

Both recordings use a 1280×720 window. Pixel analysis of the recorded
scale-4 frames detected transformed image pixels outside the three
Border regions before the fix and **0 outside after the fix**.

- Windows Border device tests: **38 passed, 0 failed, 0 skipped, 0
errors**.
- Added coverage for scale, rotation, translation, combined transforms,
shadows, nested layouts, initial asynchronous image rendering, runtime
`FontSize` and `Padding`, explicit sizing, content replacement, shape
removal, disconnect/reconnect, derived disconnect ordering, teardown
cleanup, direct layout-owner changes, derived handlers, clip ownership,
cross-host and generic-parent reparenting, composition and bounds clip
reuse, zero-by-infinite constraints, stroke-consuming zero-interior
geometry, rounded empty apertures, cross-platform arranged-size
propagation, automation, and layout-cache reuse.
- Exact final `BorderResizeContent.xaml` gallery proof ran without
resizing the window:
- Image scale 1 → 4 changed 14,673 pixels inside the Border shapes and 0
outside.
- Font size 40 → 120 changed 11,584 pixels inside the Border shapes and
0 outside.
  - All images rendered on initial load.
- The repository's production `MagickNetVisualComparer` passes all three
updated WinUI Image AspectFill snapshots at the default 0.005 RMS
threshold. The accepted delta is confined to the one-pixel Border edge
with zero changed pixels in the image interior.
- Multiple independent MAUI expert, adversarial, and layout/performance
reviews completed with no remaining actionable findings.

@jstedfast, could you please test the PR artifacts against the original
`BorderResizeContent` reproduction and confirm that initial rendering,
transformed clipping, runtime font-size changes, and resizing now behave
correctly?

Co-authored-by: Jakub Florkowski <42434498+kubaflo@users.noreply.github.com>
…'s task (#37666)

### Description of Change

Follow-up to #37409 (itself a follow-up to #37060), closing three holes
of the same species in the deferred element-scroll machinery: **a
request that ends without its task completing, or whose completion runs
on the wrong stack.** Two are permanent hangs of `await
ScrollToAsync(...)`; one is reachable by entirely ordinary code.

1. **A parked request displaced by any newer `ScrollToAsync` orphaned
the first caller's task** — two calls in a row (say, both from
`OnAppearing` before the first layout) and the first `await` never
returns. Nothing else ever completes it: the superseding request creates
a fresh completion source, and the `TaskStatus.Running` check in
`CheckTaskCompletionSource` is dead code (a TCS-backed task reports
`WaitingForActivation`, never `Running`, so the `TrySetCanceled` there
is unreachable). This is the headline fix.
2. **Yielding the replay to a re-entrant `ScrollToRequested` subscriber
abandoned the superseded caller's task** — same mechanism, narrower
trigger (a legacy renderer issuing its own `ScrollToAsync` from inside
the replayed event): permanent hang.
3. **The lifecycle-drop completion ran the caller's continuation from
inside `Element.SetParent`**, before `OnPropertyChanged(nameof(Parent))`
fires — caller code observing/mutating a half-propagated parent change.

**One primitive fixes all three.** `CompleteWithoutScroll(completion)`
captures the completion source **as a value** and posts
`TrySetResult(true)` through the dispatcher:

- **off the mutation stack** (fixes 3) — the continuation never runs
inside `SetParent`/handler-change;
- **bound by identity** (preserves the invariant from #37409's review
rounds) — the post cannot read the field at fire time, so a newer
request swapping `_scrollCompletionSource` in the window can never be
released in the old request's place;
- **applied at every point a request ends without its scroll** — the
drop paths (detach, tree-removal, orphaned target), the yielded replay
(fixes 2), and the supersede in `OnScrollToRequested` (fixes 1).

The ordinary platform-callback completion (`SendScrollFinished` when a
scroll actually happens) is untouched, so `await ScrollToAsync(...)`
timing on the normal path is unchanged. Completing (rather than
cancelling) a superseded task matches the existing drop semantics — no
`await` in this API's history has thrown on supersede, and introducing
`TaskCanceledException` would be a new public behavior.

The contract comment now names all three exits from the park — replayed,
superseded, dropped — so the stated contract and the code have no gap.

No new public API.

### Issues Fixed

Follow-up to #37409 / #37060 (fixes for #36801).

### Tests

Every fix is pinned by a test **proven to fail with that fix reverted**:

- `SupersedingAParkedRequestCompletesTheSupersededCallersTask` — both
supersede shapes (direct send, and park-in-place)
- `ReentrantRequestFromTheReplayedEventSupersedesTheReplay` — extended
to assert the superseded caller is released
- `DropCompletionIsPostedNotRunOnTheMutationStack` — dispatcher-capture
harness: task pending after `Remove` returns, exactly one post queued,
completes when it runs
-
`PostedDropCompletionReleasesOnlyTheDroppedTaskEvenIfANewRequestArrivesFirst`
— the identity property: a new request arriving before the post runs is
untouched; the dropped task completes (this test fails against a
field-reading post, the bug shape from #37409's review)

Verified on the merged `inflight/current` base: `ScrollViewUnitTests`
42/42 (10 consecutive runs), `Issue36801` + `Issue36801DeferredElement`
+ `ShellFlyoutHeaderScrollViewContent` iOS UI suites 12/12 on the
simulator.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
… static event with WeakEventManager (#37382)

<!-- 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
VisualDiagnostics.VisualTreeChanged is a public static event used by
XAML Hot Reload, live visual-tree inspectors, and diagnostic tools.
Because it is a plain multicast event, every subscriber is held by a
strong reference in the invocation list.
Any page, view, or object that subscribes stays alive until it
explicitly calls -=, silently leaking the entire visual subtree,
bindings, and view models.

### Root Cause
The event was a normal static event — .NET stores subscribers with
strong references.
Since it's static, the list lives for the app's lifetime, so every
subscriber lives forever too.
The only way to escape was to explicitly write -=, which most callers
forget.

### Description of Change
Backed the event with a WeakEventManager (MAUI's built-in weak-event
helper), so subscribers are held weakly and can be GC'd.
The public event signature is unchanged — callers don't need to change
anything.

Validated the behavior in the following platforms
 
- [x] Android
- [x] Windows
- [x] iOS
- [x] Mac
 
### Issues Fixed
  
Fixes #37242 

### Fix reference : 
[maui/src/Controls/src/Core/Application/Application.cs at
bf62313 ·
…](https://github.com/dotnet/maui/blob/bf62313db2cecf3215f10c353720b341aba0a4ee/src/Controls/src/Core/Application/Application.cs#L263)
[maui/src/Controls/src/Core/Command.cs at
bf62313 ·
dotnet/maui](https://github.com/dotnet/maui/blob/bf62313db2cecf3215f10c353720b341aba0a4ee/src/Controls/src/Core/Command.cs#L128)
[maui/src/Controls/src/Core/ImageSource.cs at
bf62313 ·
dotnet/maui](https://github.com/dotnet/maui/blob/bf62313db2cecf3215f10c353720b341aba0a4ee/src/Controls/src/Core/ImageSource.cs#L175)

### Output  ScreenShot

|Before|After|
|--|--|
| <video
src="https://github.com/user-attachments/assets/8b1d453a-dedd-48d0-a01d-aae7c7523f82"
>| <video
src="https://github.com/user-attachments/assets/824df312-de3c-4c4e-b073-9239b7c9d5c9">|
<!-- 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

On Windows, mouse-wheel scrolling could update `CarouselView.Position`
from native `ScrollViewer` events and then feed that update back into an
animated programmatic `ScrollTo`. Competing native and programmatic
scroll paths could cause runaway scrolling or leave the selected item
visibly off-center.

This change:

- suppresses only the exact `Position`/`CurrentItem` mapper callbacks
produced by native scrolling and handler-originated collection
reconciliation, while preserving application-driven nested updates and
nesting the suppression state safely;
- lets newer user/native input supersede active corrective recentering
instead of swallowing it as part of the previous request;
- treats a routed mouse-wheel event as immediate user intent, removing
the timing window that could leave stale collection state active before
`ViewChanging`;
- uses the geometrically nearest visible item for both logical selection
and terminal centering, with the native center index as a fallback while
containers are realized;
- defers terminal geometry until Windows has completed virtualized
layout and versions queued work so newer input, application scrolling,
collection updates, and disconnects invalidate stale callbacks;
- recenters when Windows scrolling settles for centered `Mandatory` and
`MandatorySingle` snap points and bounds corrective attempts when native
coercion rejects a requested center;
- preserves `KeepScrollOffset` reconciliation and synchronous
application `Position` or `CurrentItem` overrides during collection
updates, including reentrant `PositionChanged` updates;
- validates native collection projection after layout, follows
`Items.VectorChanged` progress without resetting the bounded retry
budget, realizes offscreen override targets natively, and issues one
final centered MAUI scroll;
- reuses one retry timer within a handler connection, then stops,
detaches, and releases it on disconnect so reconnect uses the new
dispatcher;
- makes repeated native `ScrollViewer` discovery idempotent and detaches
routed/native handlers symmetrically on replacement and disconnect;
- maps looped physical indexes to logical indexes, realizes looped
targets in the central physical cycle, and keeps final centering in that
high physical range without exposing `CenterMode` across asynchronous
work;
- resets all transient scroll, timer, pointer-input, initialization,
loop-facade, and native collection subscription state across superseding
input and handler disconnect/reconnect;
- applies forward tie-breaking against the true minimum distance so the
1 px tolerance cannot compound; and
- clamps target offsets at scroll boundaries.

The fix is limited to the Windows CarouselView implementation. Native
`None`, Start, and End snap behavior remains owned by WinUI.

### Validation

Validated with the issue's linked reproduction at commit
`cee7aee936214bbe7fa2a06ffec7d6d14876bb8c`, built against the final PR
sources. Rapid forward wheel bursts, delayed tail input, and rapid
reverse input each remained responsive and settled within 1 px of the
viewport center.

The Windows `CarouselView` device-test category now contains 45 tests.
Added or strengthened coverage includes repeated `ScrollViewer`
discovery, immediate wheel supersession, reentrant `PositionChanged`
collection overrides, monotonic native-projection retry budgets,
deterministic looped high-cycle realization/final centering, nested
native-scroll attribution, retry-timer reuse, and disconnect/reconnect
cleanup.

- **controlled previous production handler:** 41 passed, 4 failed; the
failures discriminate nested scroll attribution, retry-timer reuse,
disconnect cleanup (including initialization and loop-facade state), and
the redundant-layout collection path;
- **test-first timer lifecycle check:** 44 passed, 1 failed before
disconnect disposal; 45 passed, 0 failed after the fix;
- **final local unpackaged Windows CarouselView suite:** 45 passed, 0
failed;
- **final local real-packaged Windows CarouselView suite:** 45 passed, 0
failed;
- **exact linked reproduction:** all three wheel scenarios settled
within 1 px; and
- **independent MAUI expert review:** found the cross-disconnect timer
ownership issue above; re-review after its test-first fix was clean with
no remaining findings.

All test counts come from authoritative XML read after the test process
exited.

#### Before fix

The card remains visibly displaced after scrolling (measured
approximately 112 px from the viewport center).


https://github.com/user-attachments/assets/ae6c8f6b-648e-4927-9591-4af9c27bd874

#### After fix

The same interaction remains responsive and settles with the card
centered (runtime validation tolerance was <=1 px).


https://github.com/user-attachments/assets/1ea9a740-770c-4783-85c9-183e75cb9d96

### Testing Requested

@LaoR-UA, thank you for the reproduction and manual feedback. When CI
publishes artifacts for this PR, could you please [try the resulting
build](https://github.com/dotnet/maui/wiki/Testing-PR-Builds) with your
original project and let us know whether mouse-wheel scrolling now
behaves correctly on your setup?

### Issues Fixed

Fixes #35621

---------
…e members (#36444)

<!-- 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

XAML Source Generation emitted direct member access for binding paths,
so a `null` intermediate (for example, `{Binding A.B.C.Text}` where `A`
or `B` is null at runtime) threw `NullReferenceException`. This diverged
from runtime and XamlC bindings, which tolerate null intermediates.

The root cause was that the generator based null-conditional access on
nullable reference-type annotations. Those annotations are not enforced
at runtime: a reference declared non-nullable can still be `null`.

This change:

- Uses runtime nullability for member, array-index, and indexer path
parts, and propagates the resulting nullable type through generated
getters, setters, handlers, and `TypedBinding<,>` arguments.
- Keeps two-way bindings able to write `null` through null-tolerant
paths. Reference-type endpoints assign with the null-forgiving operator
to remain warning-free; non-nullable value types retain the existing
null guard.
- Handles value-type property and indexer intermediates with
read-modify-write generation. The setter updates a captured copy and
assigns it back through each property/indexer in reverse order, avoiding
both CS1612 and silently dropped writes.
- Identifies array elements from the receiver type. Array elements and
value-type fields remain direct lvalues and are not captured.

Getter output, before and after:

```csharp
// A, B, and C are non-nullable-declared reference types
getter: source => (source.A.B.C.Text, true),
getter: source => (source.A?.B?.C?.Text, true),
```

The first hop remains non-conditional (`source.Foo`, not `source?.Foo`)
because `TypedBinding.ApplyCore` invokes the getter only with a non-null
source. Non-nullable value-type endpoints retain their `?? default`
fallback so their binding property types do not change.

### Tests

- Updated exact generated-code expectations for compiled bindings,
indexers, and Maui13856.
- Added coverage for non-nullable-declared reference intermediates.
- Added coverage proving two-way paths still write `null`.
- Added setter coverage for value-type properties, indexers, arrays,
fields, and nested reverse-order copy-back.

### Issues Fixed

Fixes #36313

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: simonrozsival <374616+simonrozsival@users.noreply.github.com>
Co-authored-by: Jonathan Peppers <jonathan.peppers@microsoft.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Simon Rozsival <simon@rozsival.com>
Copilot-Session: 10f057bb-7603-49db-aaf3-707c0cf0ddff
…eservation Causing Phantom Scroll Range (#37765)

> [!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!

On iOS, a ScrollView configured with SafeAreaEdges="Container"
incorrectly reserves the safe area twice, resulting in an additional
phantom scroll range. When the content is smaller than the viewport, the
ScrollView becomes unnecessarily scrollable. The extra scrollable area
corresponds exactly to the combined safe-area insets (for example, 82 pt
on an iPhone 11: 48 pt for the status bar and 34 pt for the home
indicator).

When the content is taller than the viewport, the same issue causes an
unnecessary dead over-scroll space at the end of the content.

The issue occurs because MauiScrollView.CrossPlatformArrange always pads
the scroll content's measured size by the safe-area thickness when
ContentInsetAdjustmentBehavior is not Automatic, without checking
whether UIKit has already reserved the same space through
AdjustedContentInset.

As a result, when SafeAreaEdges="Container" is set and the native inset
is active, the safe area is accounted for twice—once by UIKit and once
by the manual padding. This inflates the content size beyond its actual
bounds and produces a phantom scrollable range, even when the content
should not be scrollable.

The fix narrows the manual-padding condition so that it applies only
when the native inset is genuinely inactive—that is, when
SystemAdjustedContentInset is zero or ContentInsetAdjustmentBehavior is
Never. In all other cases, UIKit's own inset is trusted to reserve the
safe area, and the content size is left unchanged.

This removes the double reservation for SafeAreaEdges="Container" while
preserving the existing None and Automaticbehaviors.

Tested the behavior in the following platforms.

- [x] iOS
- [x] Android
- [ ] Mac
- [ ] Windows

Fixes #36800

|Before Fix|After Fix|
|--|--|
<video
src="https://github.com/user-attachments/assets/19c064e4-4622-4d1d-992c-340a56fb948f">
|<video
src="https://github.com/user-attachments/assets/57fa7cae-6e95-4573-885d-8f2979750f38">|

---------
Co-authored-by: Jakub Florkowski <42434498+kubaflo@users.noreply.github.com>
…37648)

<!-- Please keep the note below for people who 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 whether this change resolves your
issue. Thank you!

This pull request addresses issues with the accuracy of the `CanGoBack`
and `CanGoForward` properties in the Windows WebView handler, ensuring
that navigation state is correctly reflected and not stale. It also adds
a regression test to prevent future issues with navigation history.

### Description of Change
**Fixes and improvements to WebView navigation state:**

* Updated the logic in `WebViewHandler.Windows.cs` to check
`CoreWebView2.CanGoBack` and `CoreWebView2.CanGoForward` instead of
relying solely on the WebView2 control properties, which could become
stale and cause incorrect navigation behavior.
* Enhanced the `UpdateCanGoBackForward` extension method in
`WebViewExtensions.cs` to prefer the `CoreWebView2` navigation state for
`CanGoBack` and `CanGoForward`, falling back to the control's properties
only when necessary.

**Testing and regression prevention:**

* Added a new test, `CanGoBackIsFalseAfterReturningToFirstPage`, to
verify that `CanGoBack` is set to false after navigating back to the
first page, preventing regressions related to navigation history.


<!-- 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 #37534 

### Tested the behavior in the following platforms

- [x] Windows
- [ ] Android
- [ ] iOS
- [ ] Mac


| Before Issue Fix | After Issue Fix |
|----------|----------|
| <video
src="https://github.com/user-attachments/assets/c6c4e68b-21ac-49cd-ad96-3f628c889674">
| <video
src="https://github.com/user-attachments/assets/1a0fcee7-32d4-42a6-8003-25b89087e580">
|

<!--
Are you targeting main? All PRs should target the main branch unless
otherwise noted.
-->

---------
…est (#37267)

<!-- 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, an Image does not scale up on the unconstrained axis when only
HeightRequest or WidthRequest is set—the unconstrained axis remains
capped at its native size. This is most visible with
HorizontalStackLayout + HeightRequest and VerticalStackLayout +
WidthRequest. The reverse combinations can appear to work because the
other axis is large enough to mask the same issue. On Android, the Image
scales correctly with HeightRequest and WidthRequest in all cases.

### Root Cause
- In SizeThatFitsImage (iOS), the scale factor for ScaleAspectFit images
was calculated as Math.Min(imageWidth, widthConstraint) / imageWidth.
This could never exceed 1 (since it always divides the image's own size
by itself once the constraint is larger), so an image could shrink to
fit a smaller space but could never grow beyond its native size, even
when an explicit HeightRequest or WidthRequest requested a larger size,
regardless of StackLayout orientation.

### Description of Change
- Updated SizeThatFitsImage in ImageViewExtensions.cs to accept flags
indicating whether width and height constraints are explicit, and to
allow images to scale up to requested sizes when either is set, instead
of capping at native size.
- Modified GetDesiredSizeFromHandler in ViewHandlerExtensions.iOS.cs to
pass explicitness flags to SizeThatFitsImage, ensuring correct sizing
behavior for images with explicit size requests.


### Issues Fixed
Fixes #12879

### Validated the behaviour in the following platforms
- [ ] Windows
- [ ] Android
- [x] iOS
- [x] Mac

### Output
| Before | After |
----------|----------|
| <img
src="https://github.com/user-attachments/assets/c592756b-d87f-40dd-9073-0a4e03add828">
| <img
src="https://github.com/user-attachments/assets/c884d1d2-a048-44ee-b1d9-ac59b9c29daf">
|
…37772)

<!--
!!!!!!! MAIN IS THE ONLY ACTIVE BRANCH. MAKE SURE THIS PR IS TARGETING
MAIN. !!!!!!!
-->
The issue was caused by a small view moved off-screen with TranslationY
for a full-screen Shell navigation animation. This incorrectly preserved
the top safe-area inset, creating the unwanted gap in the bottom sheet.

https://github.com/dotnet/maui/blob/19a188896c0e518c0d201db181f67166c740724d/src/Core/src/Platform/Android/SafeAreaExtensions.cs#L175

On Android, moving a view outside the screen using TranslationY
incorrectly adds top padding to the layout.

<!-- Enter description of the fix in this section -->
Updated Android vertical animation detection to treat only full-screen
views as Shell navigation transitions. Smaller translated controls, are
excluded so they no longer receive incorrect top safe-area padding.

<!-- 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 #37418

<!--
Are you targeting main? All PRs should target the main branch unless
otherwise noted.
-->

**Tested the behavior in the following platforms.**
- [x] Android
- [ ] Windows
- [x] iOS
- [ ] Mac

| Before  | After  |
|---------|--------|
| **Android**<br> <img
src="https://github.com/user-attachments/assets/9a17e25d-4b59-4d33-9b22-014bb504e3a0"
width="300" height="600"> | **Android**<br> <img
src="https://github.com/user-attachments/assets/aef1bf14-dca6-411f-94e4-c473ac3b5d0c"
width="300" height="600"> |

---------
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!

### Description of Change

On Windows, WinUI's initial focus routing can select the first focusable
descendant of a `ScrollViewer`. When a page opens with an `Entry` inside
a scrollable MAUI `ScrollView`, that can focus the `Entry` and raise its
`Focused` event without user input.

The Windows handler now makes the native `ScrollViewer` a **transient**
tab stop from WinUI's `Loading` event, giving the load-time focus pass a
non-content target. A queued callback restores the original native,
styled, locally customized, or bound `IsTabStop` state after load, so
the ScrollViewer does not become a permanent keyboard or Narrator stop.
A low-priority fail-safe queued from `Loading` also restores the
transient state if the ScrollViewer leaves the native visual tree before
either `Loaded` or `Unloaded` fires. The normal `Loaded` callback
supersedes that fail-safe through a generation guard and restores on the
next dispatcher turn.

The temporary value is represented by an owned binding: if an app or
mapper writes the native property before restore, that write replaces
the binding and wins. Generation plus platform-view identity checks
prevent stale callbacks from mutating a disconnected or replaced
handler. Cleanup and restoration intentionally happen before
`base.DisconnectHandler`, while the platform view and handler ownership
are still valid; all lifecycle events are unsubscribed before base
teardown.

The shared Windows unfocus path preserves the original
binding/local/style/default ownership of `IsTabStop`, so an unfocus
operation during the temporary window cannot convert the transient value
into a permanent local tab stop. Bindings are detached before the
temporary `false` write so TwoWay sources are not mutated, then the
original binding object is reinstated.

No public API is added or changed.

### What NOT to Do

- Do not make the internal `ContentPanel` a tab stop: it creates an
invisible, unannounced keyboard stop.
- Do not leave `ScrollViewer.IsTabStop=true` permanently: that changes
steady-state tab order and accessibility for every Windows ScrollView.
- Do not set `AllowFocusOnInteraction=false`: empirical testing showed
that it also prevents legitimate pointer focus of child Entries.
- Do not restore a hardcoded `IsTabStop` value: doing so destroys style,
binding, and custom handler/mapper ownership.
- Do not write a temporary dependency-property value while a TwoWay
binding is attached: that pushes the temporary value into the source.
- Do not use a normal-priority fail-safe from `Loading`: it can run
before WinUI reaches `Loaded`, restoring too early and reintroducing
automatic Entry focus. The fail-safe must remain low priority.
- Do not synchronously restore when the low-priority `Loading` enqueue
fails: empirical testing showed that removing the transient target
during `Loading` recreates automatic Entry focus. Dispatcher shutdown
remains covered by synchronous handler disconnect cleanup.
- Do not cancel `GettingFocus` to identify automatic focus: WinUI
reports both automatic and legitimate app requests as programmatic
focus, so that signal cannot distinguish them safely.

### Issues Fixed

Fixes #31039

### Validation

- Windows Controls `ScrollView` device tests with the fix: **26 passed,
0 failed**. Coverage includes direct transient activation, lifecycle
ordering through `Loaded`, negative startup focus with a preceding
Button and two sibling ScrollViews,
native/default/local/style/OneWay/TwoWay ownership, binding detachment,
TwoWay source and binding identity, later native writes, unfocus during
the transient window, explicit focus, disconnect/reconnect,
unload/reload, stale callbacks, native focused-element state, rooted Tab
and Shift+Tab navigation, and removal from the native visual tree during
`Loading` before `Loaded`/`Unloaded`.
- Controlled sensitivity with only production `Loading` activation
disabled: **20 passed, 6 failed**. The discriminating failures are
startup focus, preceding-control/two-ScrollView startup focus,
navigation reload, transient ordering, reverse navigation from the
startup ScrollViewer, and custom local-value restoration.
- Windows Controls `Entry` device tests: **54 passed, 0 failed, 4
skipped**. A non-ScrollView regression test verifies that programmatic
Unfocus preserves style ownership and tracks a later style replacement.
- The issue author's exact attached sample was rebuilt against the
unchanged production implementation represented by head `21d3526a1b` and
automated with UI Automation plus real pointer/keyboard input:
- startup focus is the native ScrollViewer UIA `Pane`, exposed as a
control/content element; no Entry focus and no alert;
  - clicking a normal Entry focuses it;
  - Tab advances to the next Entry;
  - Shift+Tab returns to the previous Entry;
- clicking an `InputTransparent` Entry does not raise the first Entry's
focus alert.
- Independent MAUI-specialist and adversarial lifecycle reviews found no
remaining high-confidence defect after the added fail-safe and
discriminating coverage.
- Optional device build 1565839 contained one unrelated MacCatalyst
CarouselView leak failure; its osx.26 Helix job passed. Optional UI
build 1565837 had broad cross-platform baseline failures also present on
adjacent main builds. Required main build 1565836 passed.

### Empirical Proof

**Before — untouched attached sample (MAUI 9.0.14):**


https://github.com/user-attachments/assets/fc1c44f3-e385-4808-9f00-9694b83e127f

**After — exact sample rebuilt against the transient fix:**


https://github.com/user-attachments/assets/af84a6c5-97e1-4ddd-a89f-3730748b8cf2

@Kamalesh-Periyasamy, once the updated PR artifacts are available,
please try them with your original reproduction and let us know whether
this resolves the issue for you.

---------

Copilot-Session: bcd06486-88bb-4458-ad38-55bf1be162f5
…37111)

This pull request makes several improvements to the Entry control test
pages and their supporting view models, focusing on expanding test
coverage, improving UI accessibility, and enhancing code
maintainability. The most significant changes include adding new
bindable properties (such as `BackgroundColor`, `Opacity`, and
`WidthRequest`), updating event handler logic for better testability,
and refining the ViewModel with new helpers and a reset method.
Additionally, UI elements have been updated for consistency and
accessibility.

**Feature and UI enhancements:**

* Added bindable properties `BackgroundColor`, `Opacity`, and
`WidthRequest` to the Entry controls and ViewModel, allowing these
properties to be set and tested from the UI (`EntryControlPage.xaml`,
`Material3_EntryControlPage.xaml`, `EntryViewModel.cs`).
[[1]](diffhunk://#diff-14e7f146267aafe278aab3e4cec14f9fc100e09ae21886569c33bffba87cff28R42-R44)
[[2]](diffhunk://#diff-f5e3ee56e4135c8dfd05cfce9bf234d413b52c8b6ce864a4ba0fca1ad004cc75R42-R44)
[[3]](diffhunk://#diff-3f677ed4c454a245e3e82416c1a86b24aa0ade96b3b809d4cbedd7bc75534536L28-R32)
[[4]](diffhunk://#diff-3f677ed4c454a245e3e82416c1a86b24aa0ade96b3b809d4cbedd7bc75534536R180-R213)
* Added new labels to display the current keyboard type and return type
in the UI, and updated the ViewModel to provide these values
(`EntryControlPage.xaml`, `EntryViewModel.cs`).
[[1]](diffhunk://#diff-14e7f146267aafe278aab3e4cec14f9fc100e09ae21886569c33bffba87cff28L100-R131)
[[2]](diffhunk://#diff-3f677ed4c454a245e3e82416c1a86b24aa0ade96b3b809d4cbedd7bc75534536L124-R136)
[[3]](diffhunk://#diff-3f677ed4c454a245e3e82416c1a86b24aa0ade96b3b809d4cbedd7bc75534536R180-R213)

**Event handler and logic improvements:**

* Updated event handlers to use `TextChanged` events for properties like
cursor position, selection length, max length, width request, opacity,
and height request, improving testability and code clarity
(`EntryControlPage.xaml.cs`, `EntryOptionsPage.xaml.cs`).
[[1]](diffhunk://#diff-976ce0c4d44779d336f56a5b66c3d1d02d6b59783311ee99efb4f5d1eb58317fL39-L54)
[[2]](diffhunk://#diff-07ad3578095d6b7e4ef419d6921a7d43b5319bfd2970f669cb006118d1677de7L124-R134)
[[3]](diffhunk://#diff-07ad3578095d6b7e4ef419d6921a7d43b5319bfd2970f669cb006118d1677de7L259-R318)
* Refactored color selection logic for text and placeholder colors to
use `AutomationId` instead of background color, increasing reliability
in UI tests (`EntryOptionsPage.xaml.cs`).
[[1]](diffhunk://#diff-07ad3578095d6b7e4ef419d6921a7d43b5319bfd2970f669cb006118d1677de7L26-R31)
[[2]](diffhunk://#diff-07ad3578095d6b7e4ef419d6921a7d43b5319bfd2970f669cb006118d1677de7L50-R60)
* Added a new reset method to the ViewModel to restore all properties to
their default values, and updated the navigation logic to call this
method (`EntryViewModel.cs`, `EntryControlPage.xaml.cs`).
[[1]](diffhunk://#diff-3f677ed4c454a245e3e82416c1a86b24aa0ade96b3b809d4cbedd7bc75534536R312-R348)
[[2]](diffhunk://#diff-976ce0c4d44779d336f56a5b66c3d1d02d6b59783311ee99efb4f5d1eb58317fL39-L54)

**UI and accessibility updates:**

* Added `AutomationId` values to key UI elements for improved automated
testing and accessibility (`EntryControlPage.xaml`,
`Material3_EntryControlPage.xaml`).
[[1]](diffhunk://#diff-14e7f146267aafe278aab3e4cec14f9fc100e09ae21886569c33bffba87cff28R10-L13)
[[2]](diffhunk://#diff-f5e3ee56e4135c8dfd05cfce9bf234d413b52c8b6ce864a4ba0fca1ad004cc75L8-L20)
* Adjusted font sizes for consistency and improved label clarity, and
reorganized UI elements for better structure and accessibility
(`EntryControlPage.xaml`).
[[1]](diffhunk://#diff-14e7f146267aafe278aab3e4cec14f9fc100e09ae21886569c33bffba87cff28L70-R78)
[[2]](diffhunk://#diff-14e7f146267aafe278aab3e4cec14f9fc100e09ae21886569c33bffba87cff28L100-R131)

These changes collectively improve the testability, maintainability, and
accessibility of the Entry control test pages.

Issue Identified:
- #34611
- #29805

---------

Co-authored-by: Jakub Florkowski <kubaflo123@gmail.com>
Co-authored-by: Copilot <223556219+Copilot@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!

## Description

Enables `AllowDrop` on the Windows BlazorWebView's underlying WinUI
`WebView2` so external files dragged from Explorer can reach HTML drop
targets.

This addresses the Windows external-file-drop portion of #2205. Native
in-page HTML element and image dragging remains blocked by upstream
WinUI/WebView2 behavior and is outside the scope of this change.

A cross-host repro and compatibility matrix are available at
https://github.com/danroth27/BlazorHybridDragDrop.

## Testing

- Added a Windows device test that verifies the created platform
WebView2 has `AllowDrop` enabled.
- Windows BlazorWebView device tests: 18 passed, 4 skipped, 0 failed.

Fixes #37903

Copilot-Session: 6ecbe107-8703-4352-a1c6-e4f722cb9b28
<!-- 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 ScrollView.Orientation="Horizontal" on Android, the content
unexpectedly scrolls a few pixels vertically too, even though only
horizontal scrolling was requested.

### Root Cause
Android's outer scroll container calculates vertical scroll range using
its inner horizontal-scroll child's height vs. its own padded content
area. MAUI's code sized that inner child using the full (unpadded) outer
height, leaving a small leftover gap equal to the padding — causing
phantom vertical scroll.

### Description of Change
In MauiScrollView.cs , OnMeasure / OnLayout now subtract
PaddingTop/Bottom/Left/Right when sizing the inner horizontal scroll
view, and offset its layout position by PaddingLeft/Top . This makes the
inner view exactly match the true content area, eliminating the leftover
scroll range.

Validated the behavior in the following platforms
 
- [x] Android
- [x] Windows
- [x] iOS
- [x] Mac
 
### Issues Fixed
  
Fixes #37335 

### Output  ScreenShot

|Before|After|
|--|--|
| <video
src="https://github.com/user-attachments/assets/677494c4-4e09-4e1b-af46-eeb48a3c2f7a"
>| <video
src="https://github.com/user-attachments/assets/0180fcf3-6c7e-482b-bad0-7893e0838aa9">|
<!-- 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

Marks `MauiWinUIApplication.Services` and
`MauiWinUIApplication.Application` obsolete with actionable guidance to
use `IPlatformApplication.Current` instead. This aligns Windows with the
existing Android, iOS/MacCatalyst, and Tizen API surface and removes the
overdue .NET 9 TODOs.

### Rationale

PR #16803 moved framework internals away from concrete native
application hosts to support embedding scenarios and originally
deprecated these properties. PR #17667 deferred the Windows annotations
because the WinUI XAML generator surfaced obsolete warnings from
generated accessors.

The current WinUI XAML generator now wraps generated obsolete-property
accesses in `#pragma warning disable 0618`, so default projects no
longer receive those warnings. Handwritten consumers still receive the
intended migration warning.

No `PublicAPI` baseline update is required because the property
signatures are unchanged and obsolete attributes are not represented by
the current baseline format.

### Validation

- Built `Core.csproj` for `net10.0-windows10.0.19041.0`
- Built `Core.csproj` for `net10.0-windows10.0.20348.0`
- Rebuilt the Windows Sandbox app and confirmed generated accessors
produce no `CS0618` or `RS0030` warnings

---------

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!

## Summary

- use the type-safe `Bundle.GetParcelable` overload for
`WebAuthenticatorIntermediateActivity` on Android API 33 and newer
- preserve the obsolete overload for Android API 21-32
- document why `ActivationState` must retain generic `Bundle.Get` access
to preserve arbitrary value stringification
- remove obsolete and platform warning suppressions where the typed
overload makes them unnecessary

## Validation

- `dotnet build src\Essentials\src\Essentials.csproj -f
net10.0-android36.0 --no-restore --nologo -v:minimal` — 0 warnings, 0
errors
- `dotnet build src\Core\src\Core.csproj -f net10.0-android36.0
--no-restore --nologo -v:minimal` — succeeded with only the existing
Gradle Java 8 source/target warnings
- `pwsh .github\skills\run-device-tests\scripts\Run-DeviceTests.ps1
-Project Essentials -Platform android -BuildOnly` — succeeded and
produced the signed APK

No Android emulator was available on the Windows validation host, so API
21-32 and API 33+ Activity recreation paths were not runtime-executed.
The implementation follows the existing `IntermediateActivity`
compatibility pattern.

---------

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!

## Description

Fixes #37429.

On iOS, grouped `CollectionView` used the flattened leaf-item count to
control `EmptyView` visibility. Removing the final item from an existing
group therefore displayed `EmptyView` over a group that still existed.
Grouped sources now use the outer `GroupCount`, so `EmptyView` remains
hidden while any group exists and appears only after the final group is
removed.

The fix applies to both iOS handler generations: the legacy
`CollectionViewHandler` and current `CollectionViewHandler2`. Each
handler also retains a separate leaf-item emptiness signal so
measurement and cache behavior is not coupled to group-level `EmptyView`
state.

This is the iOS-specific split of closed #37439, which maintainers
requested be separated by platform. It intentionally contains no Android
changes.

## Tests

Added iOS device tests for both handlers using eventual assertions. They
cover the reported populated-group-to-empty-group transition, verify
that `EmptyView` does not overlay the remaining empty group after its
final item is removed, verify that removing the final group shows
`EmptyView`, and verify that adding an empty group hides it again.

The iOS Debug device-test bundle builds successfully on Xcode 26.4.
Local simulator execution could not complete because the installed
XHarness 9 CLI cannot enumerate Xcode 26.4 simulators (exit code 71).

---------
@jfversluis

Copy link
Copy Markdown
Member Author

Runtime evidence for 6cc0473f2ac, recorded on iPhone 17 Pro / iOS 26.5 with the final focused sample. This is the reporter’s 19-row ListView with its bottom Close overlay, not a replacement mock-up.

The continuous recording shows collapse, three short reverse/forward pairs across the title threshold, and restoration at Item 0. The settled states retain the correct title geometry without content overlapping the expanded title. Scrolled content behind the translucent compact bar is UIKit’s native material.

Expanded Collapsed Restored at Item 0
Expanded large title and Item 0 Compact title after scrolling Restored large title with Item 0 below it

The 15-case iOS suite passed. The three primary cases fail without the fix and pass with it. A separate reviewer also exercised nested expanded/collapsed cover-and-rotation re-entry at runtime; title geometry was preserved. These checks cover the stated scenarios, not every supported device or layout.

issue33037-final-demo.mp4

@jfversluis

This comment has been minimized.

@jfversluis

This comment has been minimized.

@azure-pipelines

This comment has been minimized.

1 similar comment
@azure-pipelines

This comment has been minimized.

@github-actions github-actions Bot added the s/agent-review-in-progress AI review is currently running for this PR label Oct 20, 2026
@jfversluis

Copy link
Copy Markdown
Member Author

CI has now independently run the focused iOS regression suite at 6cc0473f2ac: all 15 Issue33037NonShell cases passed in the successful iOS Controls (vlatest) Navigation job (build 1642700, log 1974). I checked the individual test results, not just the job status: Reporter/Nested/Table collapse and restoration, both covered-rotation states, six ineligible-layout cases, both large-bar opt-outs, explicit safe-area ownership, and cached-page list transfer all passed.

The pipeline is still running, so this is not an overall green-CI claim. The completed Windows failures have matching target-branch evidence; the two iOS CollectionView1 failures are missing ios-26 snapshots and fail identically on the exact base build 1640775 (log 7959). No production changes are justified by those failures. Keeping this PR draft and continuing CI/review monitoring.

@jfversluis

Copy link
Copy Markdown
Member Author

Follow-up CI attribution at 6cc0473f2ac (overall CI is still running, not green):

  • The Android CoreCLR CollectionView1 and Lifecycle/ManualReview/Maps legs stopped before tests, while installing the API 30 emulator image: E2105 / Error on ZipFile unknown archive. This is independently present on target-branch build 1638401, log 753; PR logs 2232 and 2560 have the same package/error (target evidence). No provisioning changes belong in this iOS fix.
  • Both MacCatalyst device queues finished. I read the actual XML results for Controls, Core, Essentials, Graphics and BlazorWebView: zero failures in each suite on both queues. Both iOS device queues also finished; all actual result files were checked. Their sole named failure is ContentSizeExpandsToViewport (734 vs 700), identical to exact-base build 1640776. Android device Controls runs crashed with the same Handler is not set on element stack as that exact base; these runs are incomplete, not passing coverage.
  • Newly completed iOS Editor/shadow, ScrollView snapshot, and Shell failures match exact-base UI build 1640775. One first-attempt MeasuringEmptyScrollViewDoesNotCrash timeout has no match in five recent target runs. It passed the CI retry, a separate local run, and a fresh combined 17/17 runtime run (all 15 large-title cases plus delayed ScrollView and orientation). That is non-reproduction, not proof the timeout is pre-existing.

The combined runtime command was pwsh .github/scripts/BuildAndRunHostApp.ps1 -Platform ios -DeviceUdid 7B88EF5A-5797-465A-A193-EB0ABF44F55C -TestFilter "FullyQualifiedName~Issue33037NonShell|FullyQualifiedName~ScrollViewDelayedContentUITests|FullyQualifiedName~ScrollChangeOrientationUITests" with the previously documented local Xcode overrides: 17 passed. The separate post-test device-log collection step reported a tooling error; the test/TRX results completed successfully.

Keeping this draft and the servicing patch unchanged while the remaining jobs finish. No new public APIs or unrelated CI fixes added.

@jfversluis

Copy link
Copy Markdown
Member Author

CI update for unchanged head 6cc0473f2ac: maui-pr passed (build 1642696, completed 00:40 UTC).

The separate Android emulator package installation failures are now also confirmed independently on the current target branch: target build 1642866, log 4945 has the identical Package installation (emulator) failed ... Error on ZipFile unknown archive signature as PR logs 2656/4551. These legs never executed tests.

New persistent Android CollectionView/Label/ScrollView and macOS CollectionView failures match exact-base build 1640775, including the same missing snapshots and percentage differences. One additional Android ShadowShouldWork timeout passed its retry but remains unmatched, alongside the previously reported iOS delayed-ScrollView timeout; neither is being called a proven pre-existing flake. UI CI is still running. Keeping the patch unchanged and draft.

@kubaflo

This comment has been minimized.

@github-actions

This comment has been minimized.

@jfversluis

Copy link
Copy Markdown
Member Author

The /review tests failure occurred before test analysis, in the hosted workflow’s “Checkout PR branch” step: git fetch origin +refs/pull/39502/head:refs/remotes/origin/pr-head --depth=2 returned couldn’t find remote ref refs/pull/39502/head (failed run). A read-only remote check currently also advertises fix-33037-minimal at 6cc0473f2ac but not that pull ref. This is a checkout/reporting blocker, not a test verdict; changing the servicing patch would not correct it.

All three requested AzDO pipelines have now finished: maui-pr passed; device/UI pipelines are not green. The final Android CoreCLR Shell leg hit its 240-minute limit after a run with 132 passed / 221 failed, mostly a disposed-IApp setup cascade. Exact-base UI build 1640775 log 8303 independently shows that setup signature and the same two app-not-running diagnostics; that does not prove an identical underlying crash. Newly completed macOS Layout, Editor/shadow and ScrollView persistent failures also match the exact base. The macOS Entry VerifyEntry_WithShadow snapshot and previously noted transient failures remain unmatched, and timed-out runs leave incomplete coverage. No PR-caused correction has been established. The focused 15/15 iOS large-title CI results and published runtime evidence remain valid, but are not an overall clean-CI claim. Keeping this draft and unchanged while monitoring continues.

@kubaflo

This comment has been minimized.

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines will not run the associated pipelines, because the pull request was updated after the run command was issued. Review the pull request again and issue a new run command.

@jfversluis

Copy link
Copy Markdown
Member Author

@kubaflo Azure rejected the run request, and no new AzDO builds have queued. The head is still 6cc0473f2ac3c3f78a5e2e2837dd4ae122721ea2; I have not pushed any changes. Could you post a fresh /azp run comment? Please also trigger /azp run maui-pr-devicetests and /azp run maui-pr-uitests so the device and large-title UI coverage runs too.

This branch was previously deployed

1 inactive deployment
copilot-pat-pool — 6cc0473f Deployed Oct 20, 2026 by jfversluis via conclusion #2417
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

s/agent-review-in-progress AI review is currently running for this PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.