Skip to content

September 22nd, Candidate — .NET 10 SR12 (10.0.120) - #38779

Closed
kubaflo wants to merge 103 commits into
mainfrom
kubaflo-sr12-candidate
Closed

kubaflo wants to merge 103 commits into
mainfrom
kubaflo-sr12-candidate

Conversation

@kubaflo

@kubaflo kubaflo commented Sep 22, 2026

Copy link
Copy Markdown
Contributor

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!

SR12 candidate snapshot

Draft candidate for .NET MAUI 10 SR12 (10.0.120), following the 10.0.110 SR11 release. Tracks #38457.

  • Frozen source: inflight/current at 1aa4552fc784d4b443c5126c47ba4626712020fd.
  • Target: main, captured at 3d7ff41c8e9ee0e23c48df72b346218cb0071dd7.
  • Observed delta at capture: 103 commits ahead, 0 behind; the captured main commit is an ancestor of the snapshot.
  • Dedicated snapshot branch: kubaflo-sr12-candidate. Unlike prior candidate August 10th, Candidate #37393 (inflight/candidate -> main), this PR uses an isolated, frozen feature branch. It does not create or modify inflight/candidate, inflight/current, or any release ref.
  • Candidate contents are unchanged. eng/Versions.props retains PatchVersion=120, the existing ci.main / conditional ci.inflight prerelease labels, and StabilizePackageVersion=false by default. This is not a stabilization or final-package-version change.

Required ordering

Keep this PR in draft until the candidate has been reviewed and validated. Cut the SR12 release branch from the approved candidate/main while PatchVersion is still 120. The next-cycle version bump #38778 must merge only after that SR12 branch cut.

Validation status

The snapshot SHA, captured base ancestry, commit delta, preserved version settings, and published branch SHA have been checked. CI for this newly created candidate PR is not yet validated. No successful build/test outcome or ship-readiness claim is made by this PR; further candidate review and validation are required.

dependabot Bot and others added 30 commits September 21, 2026 19:17
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).

---------
Shalini-Ashokan and others added 12 commits September 21, 2026 19:17
<!-- 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
Removing a StateTriggerBase from VisualState.StateTriggers does not
detach the trigger. If it subscribed to a long-lived event, the
subscription can retain the trigger and its referenced objects.
For example, calling state.StateTriggers.Clear() removes the trigger
from the collection without calling its detach lifecycle.

### Root Cause
VisualState.StateTriggers uses WatchAddList<StateTriggerBase>, which
previously notified only when triggers were added. Operations such as
Clear(), Remove(), RemoveAt(), and replacement therefore removed
triggers without calling SendDetached().

### Description of Change
Updated WatchAddList<T> to support an optional removal callback and
added removal handling to VisualState. Removed triggers are detached,
their VisualState references are cleared, and active triggers are
recalculated.
Added regression coverage for Clear(), Remove(), RemoveAt(), and index
replacement to verify that each removed trigger is detached exactly
once.

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

### Output  ScreenShot

|Before|After|
|--|--|
| <img width="364" height="733" alt="image"
src="https://github.com/user-attachments/assets/e2287141-a2b5-4791-970c-a5301b266b6b"
/> | <img width="364" height="733" alt="image"
src="https://github.com/user-attachments/assets/44aa8172-94ae-4698-96bd-2959981ccb4a"
/> |
<!-- 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

Allow `Microsoft.Maui.Controls.Xaml` to participate in trimming on
modern .NET targets and remove its legacy assembly-level `[Preserve]`
attribute.

Source-generated XAML applications do not need the runtime XAML parser.
Previously, `Controls.Xaml.csproj` set
`<IsTrimmable>false</IsTrimmable>`, so the default partial-trim Android
publish copied the complete assembly and retained `XamlLoader`,
`XamlParser`, and their XML dependencies.

This change sets `IsTrimmable` only for non-netstandard targets. The
condition avoids `NETSDK1212` for the netstandard builds, where trimming
metadata is unsupported.

No replacement roots or preservation annotations were added: direct
references to runtime XAML entry points retain the parser call graph,
while unused runtime parsing is removed.

## Android size measurements

Controlled .NET 11 `dotnet new maui --sample-content` Android arm64
publishes used an isolated NuGet package cache and replaced only
`Microsoft.Maui.Controls.Xaml.dll` with locally built before/after
binaries. Both publishes used the template-default `TrimMode=partial`
and `PublishReadyToRun=false`.

| Measurement | Before | After | Delta |
|---|---:|---:|---:|
| APK | 21,375,536 B | 21,313,030 B | **-62,506 B (-0.29%)** |
| `Microsoft.Maui.Controls.Xaml.dll` | 125,952 B | 26,624 B | **-99,328
B** |
| `XamlLoader` / `XamlParser` | Present | **Absent** | Removed |
| `System.Xml.ReaderWriter.dll` | 5,120 B | 5,120 B | Unchanged |
| `System.Private.Xml.dll` | 396,288 B | 381,440 B | **-14,848 B** |
| `Microsoft.Maui.Controls.dll` | 1,414,144 B | 1,397,248 B | **-16,896
B** |

`System.Xml.ReaderWriter` remains in the default partial-trim publish,
but disappears under `TrimMode=full`. `System.Private.Xml` becomes
smaller but remains because the sample-content app has independent
references from Controls, Mono.Android, Syncfusion, AndroidX, and
application code.

The source-generated sample-content APK was installed and launched
successfully on a physical Android device after trimming removed
`XamlLoader` and `XamlParser`.

## Runtime XAML compatibility

A separate default partial-trim publish directly called:

```csharp
new ContentPage().LoadFromXaml("<ContentPage ...><Label ... /></ContentPage>");
```

The linker retained the runtime parser without assembly-wide
preservation:

- `Microsoft.Maui.Controls.Xaml.dll`: 94,720 B
- `XamlLoader` / `XamlParser`: present
- `System.Xml.ReaderWriter.dll`: 5,120 B
- `System.Private.Xml.dll`: 395,776 B

That APK also installed and launched successfully on a physical Android
device. Runtime XAML remains annotated with `RequiresUnreferencedCode`;
applications that reference types only through runtime XAML may still
need application-specific preservation. A full-trim publish reports the
existing expected `IL2026` warning at the `LoadFromXaml` call site.

Hot reload uses runtime XAML in non-trimmed development builds. Its
resource-loading tests continue to pass; no hot-reload-specific root was
required.

## Validation

- `dotnet build src/Controls/src/Xaml/Controls.Xaml.csproj -c Release -f
net10.0` — succeeded with no warnings
- `dotnet build src/Controls/src/Xaml/Controls.Xaml.csproj -c Release -f
netstandard2.0` — succeeded with no warnings
- XAML `LoaderTests` — 39 passed
- Hot-reload resource tests (`HRTests`) — 3 passed
- Default partial-trim source-generated sample-content publish and
physical Android launch
- Default partial-trim direct `LoadFromXaml` publish and physical
Android launch
- Full-trim before/after comparison confirmed the legacy
`PreserveAttribute` was not honored as a whole-assembly root by the
current linker

---------
…or Shell/TabbedPage appearance under Material 3 (#37147)

<!--
!!!!!!! MAIN IS THE ONLY ACTIVE BRANCH. MAKE SURE THIS PR IS TARGETING
MAIN. !!!!!!!
-->
### Issue Description
When Material 3 is enabled via <UseMaterial3>true</UseMaterial3>,
Android Shell top tabs correctly show the selected state, but the
selected indicator color does not match the Material 3 tabs
specification.

### Root Cause

Android Shell/TabbedPage rendering (`ShellToolbarAppearanceTracker`,`
ShellTabLayoutAppearanceTracker`, `ShellBottomNavViewAppearanceTracker`,
`TabbedPageManager`) fell back to hardcoded default colors whenever a
Shell.TabBar*/appearance color property was null.

Under Material 3, this hardcoded fallback overrode the native M3 theme's
own default colors (colorPrimary, colorOnSurface, etc.), so M3 apps
never displayed correct theme colors on the Shell toolbar, top tabs
(TabLayout), or bottom navigation (BottomNavigationView) unless every
single color was explicitly set.

### Description of Change

- Each Android tracker captures the platform view's
native/theme-provided color or background once, before any custom
appearance is ever applied to it.
- When a `Shell.TabBar*` color is later cleared back to null (e.g.
removing a custom appearance), the tracker restores the
originally-captured native value instead of substituting a hardcoded
color.
- This means M3 apps now correctly fall back to their native theme
colors — matching platform conventions — rather than being forced into
an unrelated hardcoded palette.

### Issues Fixed
Fixes #35126 

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

### Output Screenshot
Case| Light Theme | Dark Theme |
|----------|----------|----------|
| **Before**|<image width="100" height="200" alt="Image"
src="https://github.com/user-attachments/assets/e8a6cc5f-5101-4d8d-9f0b-b199710b5da8">|<image
width="100" height="200" alt="After Fix" src
="https://github.com/user-attachments/assets/23b6e57c-34fd-48d3-8c92-0b18f2df4ea9">|
| **After** |<image width="100" height="200" alt="Image"
src="https://github.com/user-attachments/assets/d732e3bc-6a53-4cd8-93a9-c559576d0d5c">|<image
width="100" height="200" alt="After Fix" src
="https://github.com/user-attachments/assets/9c6ebe0f-b7ce-4d24-8d76-cad6f25cd785">|
<!-- 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!

### Root Cause

The default `NavigationPage` and `TabbedPage` styles in `Styles.xaml`
were not configured to apply to derived types. As a result, custom pages
inheriting from these controls did not receive the default style setters
and theme bindings.
<!-- Enter description of the fix in this section -->

### Description of change

- Added `ApplyToDerivedTypes="True"` to the default `NavigationPage`
style.
- Added `ApplyToDerivedTypes="True"` to the default `TabbedPage` style.

This ensures that derived pages receive the expected default colors and
AppThemeBinding values, including updates when the application theme
changes.
### 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 #38400 

### 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/b706345f-1fd2-4fcc-8fe0-36388bddfc81">
| <video
src="https://github.com/user-attachments/assets/af009220-a470-4ce4-82b0-e294895f0756">
|
<!--
Are you targeting main? All PRs should target the main branch unless
otherwise noted.
-->
<!-- 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!

### Root cause : 

- `UpdateTabBarVisibility()` was always assigning
`MauiNavigationView.PaneDisplayMode`, even when the requested value was
already the current value.
- Reassigning the same value unnecessarily causes the navigation view to
process the pane-display-mode change again.
- This interferes with the end portion of the TabBar transition
animation. The start animation can be observed, but the redundant
`PaneDisplayMode` update prevents the transition from completing
normally.
### Description of change

- Added a check in `UpdateTabBarVisibility` to compare the current
`PaneDisplayMode` with the requested value before updating it.
- This prevents redundant native property updates and allows the TabBar
transition to complete normally, including the end animation.

### Why Test not added ?
- Automated test is not added because the indicator’s end animation
cannot be captured reliably through automation. Manual verification is
required to validate the final animation state

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

### 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/63862d31-4ebb-4535-878d-59dafeb1e7c9">
| <video
src="https://github.com/user-attachments/assets/c68fbfe2-9905-4d89-a8f3-a868cfae7d36">
|
<!--
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!

### Description of Change

This change lets the XAML source generator compile common `{OnIdiom
...}` markup-extension usages as the existing strongly typed
`OnIdiom<T>` type.

For example:

```xml
<Label Text="{OnIdiom Default=Default, Phone=Phone}" />
<Grid Margin="{OnIdiom '1,2,3,4', Desktop='5,6,7,8'}" />
```

SourceGen can determine the target property types (`string` and
`Thickness`) and generate typed `OnIdiom<string>` and
`OnIdiom<Thickness>` instances instead of constructing
`OnIdiomExtension` and calling `ProvideValue` at runtime.

### Motivation

`OnIdiomExtension` must discover the target property type and perform
conversion at runtime. Its constructor is therefore annotated with
`RequiresUnreferencedCode`:

> The OnIdiomExtension is not trim safe. Use OnIdiom<T> instead.

When SourceGen emits `new OnIdiomExtension()` for ordinary XAML such as
template styles, full-trim and NativeAOT builds report IL2026 even
though the target type is already known to the XAML compiler.

This became more visible after #38613 allowed unused runtime XAML
infrastructure to be trimmed from source-generated applications. The
compiler should avoid retaining and warning about the reflection-based
path where it can produce equivalent typed code instead.

### Implementation

A new `InferOnIdiomTypeArgumentsVisitor` runs after markup expansion and
`OnPlatform` simplification, but before values are created. It:

1. Finds parsed `OnIdiomExtension` nodes used as property values.
2. Resolves the target CLR property or `BindableProperty` type,
including attached properties and `Setter.Value`.
3. Verifies that the usage is within a conservative, behavior-preserving
subset.
4. Replaces the node with a constructed `OnIdiom<T>` node registered in
SourceGen's type cache.
5. Lets the existing SourceGen conversion and property-assignment
pipeline emit the typed values.

The generated code therefore avoids constructing `OnIdiomExtension`,
avoids its service provider and runtime property discovery, and does not
produce the associated IL2026 warning.

### Supported inference cases

Inference is intentionally limited to target types whose conversion
behavior SourceGen owns and can reproduce without consulting arbitrary
application code:

- C# scalar types such as `string`, integral types, `float`, `double`,
`decimal`, `bool`, `char`, and `DateTime`;
- `TimeSpan` and `Uri`;
- enums defined by MAUI assemblies;
- built-in MAUI types with a type-level converter already implemented by
SourceGen, such as `Thickness`, `GridLength`, `CornerRadius`, `Color`,
`Point`, `Rect`, `LayoutOptions`, and similar known types;
- numeric values on MAUI font-size properties using the exact built-in
`FontSizeConverter`.

The tests cover CLR properties, bindable properties, attached
properties, style setters, nested simplified `OnPlatform`, built-in MAUI
converters, and numeric literal suffixes for `float`, `double`,
`decimal`, `uint`, `long`, and `ulong`.

### Limitations and deliberate fallbacks

SourceGen keeps using `OnIdiomExtension` when it cannot prove the typed
transformation is equivalent. These cases continue to produce the
existing trim warning so the application can decide whether to rewrite
the XAML explicitly.

#### Custom target types

Custom application and third-party types are not inferred, even if they
expose an implicit conversion from `string`. Such types can have
conversion behavior that is not represented by SourceGen's built-in
conversion pipeline.

#### Custom property converters

Properties with custom `TypeConverter` attributes are not compiled to
`OnIdiom<T>`. A custom converter may attach semantics to otherwise
ordinary values—for example, a converter could scale `"10"`, resolve a
resource, or depend on `IProvideValueTarget`. Directly assigning `10` to
`OnIdiom<double>` would silently bypass that behavior.

The only property-level converter exception is MAUI's exact
`FontSizeConverter`, and only when every value is an invariant numeric
literal. Named font sizes such as `Medium` and `Large`, empty values,
and expressions remain on the runtime extension path.

#### Source-generated bindable properties in setters

For `Setter.Value`, inference requires an actual resolved
`BindableProperty` symbol. The existing heuristic for bindable
properties produced by another source generator can identify the
property name and type, but cannot reliably preserve all converter
metadata, so those setters deliberately fall back.

#### Runtime markup values and converters

Nested markup extensions such as bindings or resources, explicit
`Converter`/`ConverterParameter`, object-typed targets, and usages
without a `Default` value remain `OnIdiomExtension` usages.

A missing `Default` is significant because `OnIdiomExtension` can return
a bindable property's registered default value, while `OnIdiom<T>`
returns `default(T)`.

### Behavioral compatibility

`OnIdiom<T>` previously treated every unrecognized idiom as Phone, while
`OnIdiomExtension` returned `Default` for `DeviceIdiom.Unknown` and
custom idioms created with `DeviceIdiom.Create(...)`.

Because SourceGen now substitutes one implementation for the other, this
PR aligns `OnIdiom<T>` with the markup extension: Phone is selected only
for `DeviceIdiom.Phone`; unknown and custom idioms use `Default`.

### What NOT to Do

- Do not infer every type merely because SourceGen can emit a
syntactically valid assignment. Runtime converters may have additional
semantics.
- Do not map `object` targets to `OnIdiom<object>`; the standard
reference conversion assigns the wrapper rather than invoking the
generic implicit operator.
- Do not duplicate the reflection-based implementation in a new
`OnIdiomExtension<T>`. The existing `OnIdiom<T>` is sufficient for the
safe subset and avoids maintaining two extension implementations.
- Do not infer setter types from bindable-property heuristics unless
converter metadata can also be proven complete.

### Related work

- Closes #22142, which proposed compiling non-generic `{OnIdiom}` usages
into `OnIdiom<T>` when the target type is known.
- Supersedes the closed WIP #28700. That approach modified legacy IL
XamlC and introduced a duplicated generic markup-extension
implementation; this PR instead targets the current source generator and
reuses the existing public `OnIdiom<T>` type.
- #21927 originally enabled trimming analysis in
`Microsoft.Maui.Controls.Xaml` and identified this follow-up.
- #38613 made unused runtime XAML code trimmable, increasing the value
of keeping source-generated applications off reflection-based
markup-extension paths.
- Related to the broader trimming effort tracked by #18658.

### Issues Fixed

Closes #22142

### Tests

- SourceGen unit suite: 228 passed with the local .NET 11 target
override; the three net11-specific `AppThemeBinding` golden-output tests
were excluded because the project normally targets .NET 10 and the
machine does not have the .NET 10 runtime.
- Focused `OnIdiom<T>` runtime fallback test passed for Tablet, Watch,
Unknown, and a custom idiom.
- Added SourceGen regression coverage proving unsupported/custom
conversion paths retain `OnIdiomExtension`.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
…8587)

<!-- 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
On Android with Material 3, an Editor starts at its maximum height
instead of growing as text is entered.

### Root Cause
The Editor was forced to use all available height, changing
MaximumHeightRequest from a limit into its starting height.

### Description of Change
Force the available height only for single-line controls such as Entry,
allowing the multiline Editor to grow with its content.

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

### Output  ScreenShot

|Before|After|
|--|--|
| <img width="363" height="657" alt="image"
src="https://github.com/user-attachments/assets/3dc925f8-5621-44d7-95ea-39b9e3bdd19b"
/> | <img width="363" height="657" alt="image"
src="https://github.com/user-attachments/assets/e085a680-f78c-4ce3-8f83-351569155f74"
/> |
<!-- 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

`ShadowTypeConverter` constructs a runtime `Regex` whenever it parses a
shadow string. That reference contributes to retaining
`System.Text.RegularExpressions` and additional regex implementation
code in trimmed MAUI Android apps.

### Broader goal and related work

This PR is one step toward removing the roughly 300 KB linked
`System.Text.RegularExpressions` assembly from MAUI apps that do not use
regular expressions in their own application logic. Removing the
`ShadowTypeConverter` root is not sufficient by itself because other
framework and dependency roots still retain regex code.

Related work toward that goal includes:

- #38616 trims disabled MAUI Aspire support, including
`Microsoft.Maui`'s source-generated `LocalhostRegex`, when the Aspire
feature switch is off.
- dotnet/android#12813 avoids unnecessary XML marshalling references in
trimmable type-map dispatch assemblies. This helps remove XML assemblies
where they are not otherwise needed; `System.Private.Xml` contains a
generated `LanguageRegex` implementation that can independently retain
regex code.

Additional roots may still need to be removed or made trimmable before
`System.Text.RegularExpressions` disappears entirely from a given app.
For example, the measured sample-content app also reaches regex through
`CommunityToolkit.Maui`, `System.Data.Common`, and `System.Private.Xml`.

### Description of Change

Replaces the runtime `Regex` with an allocation-conscious inline
tokenizer/parser.

The parser intentionally preserves the existing unanchored
`Regex.Matches` behavior, including supported color forms,
invariant-culture decimal/scientific numbers, Unicode whitespace,
ignored punctuation, greedy 3-to-8 digit hex matching, and rollback for
incomplete decimals or exponents.

Token boundaries are stored in a stack-allocated `Span<Part>`, and only
matched tokens are materialized as strings for the existing color and
numeric parsers.

The project now also bans the `System.Text.RegularExpressions` namespace
through a `Controls.Core`-specific `BannedSymbols.txt`, preventing this
dependency from being reintroduced.

### Performance considerations

Vectorizing the character scans was considered but intentionally not
implemented. Shadow strings are normally short (roughly tens of
characters) and whitespace runs are generally zero to two characters, so
SIMD setup and branching are unlikely to beat the straightforward scalar
loops. A vectorized path would also require separate `netstandard`
fallbacks and careful tables to preserve the old regex's Unicode `\s`
and `\d` behavior, adding disproportionate complexity without
demonstrated benefit.

### Tests

- Added focused coverage for every supported color syntax,
offsets/radius/opacity, whitespace, malformed values, invariant culture,
and legacy regex edge cases.
- `ShadowTests`: 85 passed.
- `Controls.Core` builds for `netstandard2.0`, `netstandard2.1`, and
`net10.0`.
- Verified an intentional `System.Text.RegularExpressions.Regex` usage
in `Controls.Core` fails with RS0030.

### Android size measurement

Published identical stock `.NET 11 preview 7` `dotnet new maui
--sample-content` apps in Release with `TrimMode=partial`, replacing
only the `Microsoft.Maui.Controls.dll` input in the patched build.

| Artifact | Baseline | Patched | Delta |
|---|---:|---:|---:|
| Signed APK | 36,903,788 B | 36,899,692 B | **-4,096 B** |
| Unsigned APK | 36,774,978 B | 36,771,221 B | **-3,757 B** |
| Linked `Microsoft.Maui.Controls.dll` (per ABI) | 1,495,552 B |
1,495,040 B | **-512 B** |
| Linked `System.Text.RegularExpressions.dll` (per ABI) | 334,336 B |
330,752 B | **-3,584 B** |
| arm64 assembly store | 7,689,840 B | 7,688,216 B | **-1,624 B** |
| x64 assembly store | 7,899,160 B | 7,896,448 B | **-2,712 B** |

`System.Text.RegularExpressions` remains retained by other roots in this
sample: `CommunityToolkit.Maui`, `Microsoft.Maui` (`LocalhostRegex`),
`System.Data.Common`, and `System.Private.Xml`. This change removes the
`Microsoft.Maui.Controls` reference and reduces the linked regex
implementation, but does not eliminate the assembly from this app.

### Issues Fixed

None.

---------

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
…38665)

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

Fix the two Android API 36 / Pixel 3 XL SafeAreaEdges failures in [build
1602666](https://dev.azure.com/dnceng-public/public/_build/results?buildId=1602666&view=logs&j=217a4b68-6397-5ebe-0d4b-1177e8401828&t=3a87bec2-0591-537a-1f29-40eb70176265).

- Preserve the full top inset throughout a full-height page's navigation
transition, including `0 < viewTop <= topInset`. Check animation before
partial overlap, and preserve the translated-child height guard from
#38295.
- Replace catch-and-retry NUnit assertions in
`SafeAreaNoWhiteSpaceAfterKeyboardDismissAndEdgeToggle` with delayed
constraints. NUnit 4.0.1 records an assertion failure before throwing,
so catching it and succeeding on a later attempt still leaves the test
failed. The original CI device log actually shows the grid shrinking
from 2705 to 1613 pixels and returning to 2705 after dismissal.
- Preserve the 15-second polling window, 500-ms interval, keyboard
visibility checks, and one-pixel restoration tolerance. Check restored
height and Y together on the same rectangle.
- Add six Android native cases exercising the production inset helper:
full-height offsets 0, 1, 99, 100, and 101 with a 100-pixel top inset,
plus a translated short child. Assert padding and inset consumption.

No snapshots, screenshot tolerances, public APIs, dependencies, or CI
configuration are changed. `LayoutShouldBeCorrectOnFirstNavigation`
remains unchanged.

### Regression origins

| Failure | Introducing change | Evidence |
| --- | --- | --- |
| Persistent incorrect top inset during navigation | #33788, commit
`9c0887b8c343a1a98762acfccb64190355e175ee` | Removed the deferred inset
reapplication added in #33285 and introduced `viewTop > top`, leaving
the final `0 < viewTop <= top` transition range unprotected. |
| Transient keyboard measurements retained as NUnit failures | #31022,
commit `0371d318282cc6f0cbb96d36315e7fc2f70680ff` | Introduced
catch-and-retry `RetryAssert` while the Android UI project already
referenced NUnit 4.0.1. |
| The specific keyboard test exposing that defect | #35083 | Added
`SafeAreaNoWhiteSpaceAfterKeyboardDismissAndEdgeToggle` using that
helper. The later keyboard waits in #37741 did not isolate recorded
NUnit failures. |

These are source-history attributions, not a historical full-build
bisect. The keyboard failure is a test-harness defect, not proof that
the grid never resized.

### Validation

Tested commit: `8aa80ab8d9312be1aeddfbb200ff580e5c678598`.

Environment: dedicated Android API 36 / Pixel 3 XL ARM64 emulator,
1440x2960 at density 560, Release HostApp, existing NUnit 4.0.1 /
adapter 4.5.0. Actual TRX and xUnit XML results were inspected, not
inferred from process exit codes.

| Coverage | Result |
| --- | --- |
| `SafeAreaNoWhiteSpaceAfterKeyboardDismissAndEdgeToggle` | Passed in
**3 consecutive focused runs**, on the first attempt each time. |
| `LayoutShouldBeCorrectOnFirstNavigation` | Passed in **3 consecutive
focused runs**, on the first attempt each time, against the unchanged
snapshot and tolerance. |
| Six native inset boundary cases, without native fix | Three expected
failures: offsets 1, 99, and 100 produced padding 99, 1, and 0 instead
of 100. Settled-origin, offset-101, and translated-child controls
passed. |
| Same six native cases, with native fix | **6 passed, 0 failed**. |
| Complete `Issue28986_ContentPage` fixture, final with-fix A/B leg |
**5 passed, 0 failed**, first attempt. |
| Standalone NUnit 4.0.1 probe | All five expected outcomes confirmed:
caught assertions remain failed; delayed height and compound bounds
constraints recover; permanently incorrect height or bounds still fail.
|

The repository `verify-tests-fail.ps1` finished with **`VERIFICATION
PASSED`**, using frozen base `49b62062536b4a2b957d965f76035eb56806615c`
and restoring only the two fix files. The final native A/B evidence is
deterministic. The without-fix keyboard fixture was timing-dependent and
also encountered element-lookup failures; its logs contain the original
retained `2705` assertion. The standalone NUnit probe independently
isolates that bookkeeping defect. No claim is made that every
broad-fixture failure was caused by this PR's targeted bug.

Initial verification attempts were blocked by the pinned XHarness
runtime and its default instrumentation selection. They are not counted
as native proof. Final native execution used the manifest's explicit
`com.microsoft.maui.core.devicetests.TestInstrumentation` with a
session-local runtime. No repository tool versions or machine-wide SDKs
were changed.

Focused UI command, executed three times:

```powershell
pwsh .github/scripts/BuildAndRunHostApp.ps1 -Platform android -Configuration Release -DeviceUdid emulator-5554 -TestFilter 'FullyQualifiedName~Issue33038|FullyQualifiedName~SafeAreaNoWhiteSpaceAfterKeyboardDismissAndEdgeToggle'
```

The worktree was clean after verification. PR CI is separate: [maui-pr
build
1602977](https://dev.azure.com/dnceng-public/public/_build/results?buildId=1602977)
is running; this is not a full-CI-green or full-suite-green claim. No
`maui-pr-uitests` build had been scheduled at the time of this update.

### Related work and constraints

The native correction follows the narrowly scoped approach proposed in
closed, unmerged #38641, without its unrelated MacCatalyst skip. Open
#37953 proposes a broader SafeArea redesign; this PR keeps a small
correction and fixes the separate NUnit bookkeeping defect.

Do not replace the navigation baseline, widen its tolerance, or remove
the translated-child guard to make the test pass. Do not use caught
NUnit assertions as a polling predicate. The generic Appium helper
remains unchanged here to avoid adding an NUnit dependency or broad
test-infrastructure changes.

### Issues Fixed

Fixes #37555
Fixes #37556
Refs #33038
Refs #38135

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
…nect (#38653)

<!-- 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 a CollectionView has an EmptyView, MAUI posts a layout callback to
size it.
If the handler is disconnected before that callback runs, the code still
tries to access PlatformView. it crashes the application.

### Root Cause
Android queues the empty-view layout work to run during the next UI-loop
cycle. Meanwhile, navigation can disconnect the handler and clear its
PlatformView. The queued callback later reads PlatformView, causing the
“PlatformView cannot be null here” exception.

### Description of Change
The native RecyclerView is saved before scheduling the delayed
empty-view layout update, so the callback does not access the handler
after it disconnects.
The callback safely uses this saved view to find the empty-view item and
request a new layout, preventing the PlatformView cannot be null here
crash.

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

### Output  ScreenShot

|Before|After|
|--|--|
| <video
src="https://github.com/user-attachments/assets/b61c5b9c-cb57-400d-9f82-64bb08a68b4f"
>| <video
src="https://github.com/user-attachments/assets/fa9e3ab3-cd52-4eca-8966-489f5d753613">|

---------

Co-authored-by: Jakub Florkowski <kubaflo123@gmail.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
…#38651)

<!-- Please let the below note in for people that find this PR -->
> [!NOTE]
> Are you waiting for the changes in this PR to be merged?
> It would be very helpful if you could [test the resulting
artifacts](https://github.com/dotnet/maui/wiki/Testing-PR-Builds) from
this PR and let us know in a comment if this change resolves your issue.
Thank you!

<!--
!!!!!!! MAIN IS THE ONLY ACTIVE BRANCH. MAKE SURE THIS PR IS TARGETING
MAIN. !!!!!!!
-->


### Issue Details
label?.SetBinding(...) should create a compiled binding when label is
not null.
Instead, MAUI did not generate the required interceptor, so the app
compiled but threw Call to SetBinding<...> was not intercepted at
runtime.

### Root Cause
label.SetBinding(...) and label?.SetBinding(...) have different C#
syntax-tree shapes.
The MAUI generator recognized only the ordinary call shape, so it
skipped null-conditional calls and did not generate an interceptor.

### Description of Change
GetInvokedMethodName now recognizes both syntax types.
Therefore, ordinary and null-conditional SetBinding calls follow the
same interceptor-generation process.

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

### Output  ScreenShot

|Before|After|
|--|--|
| <video
src="https://github.com/user-attachments/assets/0c5a735d-4a06-4261-8488-f19f464a2be9"
>| <video
src="https://github.com/user-attachments/assets/b700be4b-776f-4def-bbd2-8325d1007bd1">|
…s not reactive at runtime (#38551)

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

### RootCause: 

In `GesturePlatformManager.iOS.cs`, the native `UITapGestureRecognizer`
is created once in `CreateTapRecognizer` with the `NumberOfTapsRequired`
value snapshotted at creation time. The PropertyChanged handler
OnTapGestureRecognizerPropertyChanged only reacted to
TapGestureRecognizer.ButtonsProperty changes (triggering
LoadRecognizers()), so changing NumberOfTapsRequired at runtime never
propagated to the native recognizer. (Android already reads
NumberOfTapsRequired dynamically on each tap via a predicate filter, so
it wasn't affected.)
 
### Fix Description:

Mirrored the existing `OnSwipeGestureRecognizerPropertyChanged` pattern
— when NumberOfTapsRequiredProperty changes, look up the already-created
native `UITapGestureRecognizer` instances in `_gestureRecognizers` and
update NumberOfTapsRequired directly, since UIKit allows changing this
property on a live recognizer.

### Issues Fixed
Fixes #38291 

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

### Output Screenshot
Before Issue Fix | After Issue Fix |
|----------|----------|
|<video width="100" height="100" alt="Before Fix"
src="https://github.com/user-attachments/assets/24f29bdd-e499-474a-9f38-adf317cb3add">|<video
width="100" height="100" alt="After Fix"
src="https://github.com/user-attachments/assets/c8d078fb-c5c4-4762-a559-5f8c885bfb52">|
@kubaflo
kubaflo deployed to copilot-pat-pool September 22, 2026 16:41 — with GitHub Actions Active
@github-actions

Copy link
Copy Markdown
Contributor

🚀 Dogfood this PR with:

⚠️ WARNING: Do not do this without first carefully reviewing the code of this PR to satisfy yourself it is safe.

curl -fsSL https://raw.githubusercontent.com/dotnet/maui/main/eng/scripts/get-maui-pr.sh | bash -s -- 38779

Or

  • Run remotely in PowerShell:
iex "& { $(irm https://raw.githubusercontent.com/dotnet/maui/main/eng/scripts/get-maui-pr.ps1) } 38779"

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 1 pipeline(s).
There may be pipelines that require an authorized user to comment /azp run to run.

@kubaflo

kubaflo commented Sep 22, 2026

Copy link
Copy Markdown
Contributor Author

Superseded by #38782, which uses the required dotnet/maui inflight/candidate -> main source branch. Both contain the exact frozen snapshot 1aa4552. No candidate contents were changed. Continue candidate review and validation in #38782.

@kubaflo kubaflo closed this Sep 22, 2026
@kubaflo
kubaflo deployed to copilot-pat-pool September 22, 2026 16:47 — with GitHub Actions Active
@kubaflo
kubaflo deployed to copilot-pat-pool September 22, 2026 16:47 — with GitHub Actions Active
@kubaflo
kubaflo deployed to copilot-pat-pool September 22, 2026 16:49 — with GitHub Actions Active
@kubaflo
kubaflo deployed to copilot-pat-pool September 22, 2026 16:50 — with GitHub Actions Active
@kubaflo
kubaflo deployed to copilot-pat-pool September 22, 2026 16:51 — with GitHub Actions Active

This branch was successfully deployed

1 active deployment
copilot-pat-pool — 1aa4552f Deployed Sep 22, 2026 by kubaflo via conclusion #2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.