Skip to content

[iOS] Fix FlyoutHeaderScroll device test regression#34932

Merged
kubaflo merged 24 commits intodotnet:inflight/candidatefrom
kubaflo:fix/flyout-header-scroll-test
Apr 14, 2026
Merged

[iOS] Fix FlyoutHeaderScroll device test regression#34932
kubaflo merged 24 commits intodotnet:inflight/candidatefrom
kubaflo:fix/flyout-header-scroll-test

Conversation

@kubaflo
Copy link
Copy Markdown
Contributor

@kubaflo kubaflo commented Apr 13, 2026

Note

Are you waiting for the changes in this PR to be merged?
It would be very helpful if you could test the resulting artifacts from this PR and let us know in a comment if this change resolves your issue. Thank you!

Description of Change

Fixes the FlyoutHeaderScroll device test regression introduced by PR #28713.

After PR #28713 added IPlatformMeasureInvalidationController to ShellFlyoutHeaderContainer, the header height after scrolling may include the safe area margin (~44px) depending on the content type. This is correct behavior — InvalidateMeasure properly re-measures the header container including its safe area margin.

Changes (test only, no core code changes):

  • Height assertion: accept header height between headerRequestedHeight and headerRequestedHeight + safeAreaTop (accommodates both ShellItems which don't trigger OnScrolled, and CollectionView/ScrollView which do)
  • Scroll position assertion: use scrolledBox.Height instead of hardcoded headerRequestedHeight, making it self-consistent with the actual measured height
  • Fix typos in assertion messages

Issues Fixed

Fixes FlyoutHeaderScroll device test regression from #28713

mattleibow and others added 19 commits April 8, 2026 22:29
…otnet#32491)

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

Fixes a critical bug where `HybridWebView.InvokeJavaScriptAsync` would
timeout when passing JSON strings as parameters. The issue was caused by
improper JavaScript string escaping. The fix extracts business
logic into a new `HybridWebViewHelper` class and uses
`WebViewHelper.EscapeJsString` for proper escaping.

   **Fixes**: dotnet#32438

   ---

   ## The Problem

Issue dotnet#32438 reported that passing JSON strings to
`InvokeJavaScriptAsync` caused timeouts on Windows (required base64
encoding as workaround), while Android worked correctly.

   ### Root Cause

The old `EvaluateJavaScriptAsync` implementation used naive string
concatenation:

   ```csharp
   // OLD - Breaks when script contains quotes
   script = "try{eval('" + script + "')}catch(e){'null'};";
   ```

When the script contained JSON with quotes, this produced invalid
JavaScript:
   ```javascript
try{eval('window.HybridWebView.__InvokeJavaScript(1, 'method',
["{\"userId\":\"value\"}"])')}catch(e){'null'};
              ↑ BROKEN - conflicting quotes
   ```

Result: JavaScript execution failed, causing `InvokeJavaScriptAsync` to
timeout.

   ---

   ## The Solution

   ### Core Fix

   New implementation properly escapes JavaScript strings:

   ```csharp
   // NEW - Uses proper escaping
   var escapedScript = WebViewHelper.EscapeJsString(script);
   var wrappedScript = $$"""
   (function() {
       try {
let result = eval('{{escapedScript}}'); // ← Properly escaped
return JSON.stringify({ IsError: false, Result: JSON.stringify(result)
});
       } catch (error) {
           // ... error handling
       }
   })()
   """;
   ```

   ### Refactoring

Extracted ~360 lines from `HybridWebViewHandler` into new
`HybridWebViewHelper` class:
- `ProcessEvaluateJavaScriptAsync` - Script wrapping with proper
escaping
   - `ProcessInvokeJavaScriptAsync` - JavaScript call building
   - `ProcessInvokeDotNetAsync` - .NET method invocation from JS
   - `ProcessRawMessage` - Message routing

   Handler reduced from ~600 to 244 lines.

   ---

   ## Changes

   **New Files:**
   - ✨ `HybridWebViewHelper.cs` (470 lines) - Centralized business logic

   **Modified Files:**
   - 🔧 `HybridWebViewHandler.cs` - Delegates to helper
   - 🔧 `HybridWebViewHandler.Standard.cs` - Updated message processing
   - 🔧 `HybridWebViewHandler.Tizen.cs` - Updated message processing

   **Tests:**
   - 🔧 `HybridWebViewTestsBase.cs` - Added 15-second timeout
   - ✨ Added 5 new tests for JSON parameter scenarios
   - ✨ Added 3 enhanced quote-handling tests
   - 🔧 Renamed 20+ tests for clarity
   - 📝 Corrected error messages to use `InvokeJavaScriptAsync`

   **Test HTML:**
- Added `EchoJsonParameter`, `ParseAndStringifyJson`,
`ConcatenateJsonStrings`, `DecodeBase64AndEcho`, `CountJsonArrayItems`

   ---

   ## Testing

   **Platforms**: iOS, Android, Windows, MacCatalyst

   **Scenarios**:
   - JSON with quotes and special characters
   - Complex nested JSON
   - Multiple JSON parameters
   - Large arrays (100 items)
   - Base64 workaround (backward compatibility)

   ---

   ## Breaking Changes

   **None** - Fully backward compatible:
   - ✅ Existing code works unchanged
   - ✅ Base64 workaround still works
   - ✅ No public API changes

   ### Migration Note

   If using base64 workaround, you can now simplify:

   ```csharp
   // OLD workaround (still works):
   var json = JsonSerializer.Serialize(obj);
   var base64 = Convert.ToBase64String(Encoding.UTF8.GetBytes(json));
await hybridWebView.InvokeJavaScriptAsync<string>("fn", ..., [base64],
...);

   // NEW (now works directly):
   var json = JsonSerializer.Serialize(obj);
await hybridWebView.InvokeJavaScriptAsync<string>("fn", ..., [json],
...);
   ```

   ---

   ## Checklist

   - [x] Code follows .NET MAUI conventions
   - [x] 8 new/enhanced tests added
   - [x] 20+ tests renamed for clarity
   - [x] No breaking changes
   - [x] Backward compatible
   - [x] Error messages corrected
   - [x] Formatted with `dotnet format`
   - [x] XML documentation complete
   - [x] Tested on multiple platforms

---------

Co-authored-by: Copilot <198982749+Copilot@users.noreply.github.com>
Co-authored-by: mattleibow <1096616+mattleibow@users.noreply.github.com>
… on collection update (dotnet#31275)

<!-- 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
**Windows**
Position not updating on item add: CarouselView stayed at the old
position after an item was added, leaving current/previous positions
unsynced.

Cascading events: With `ItemsUpdatingScrollMode.KeepItemsInView`,
programmatic smooth scrolls triggered multiple ViewChanged calls,
causing `PositionChanged` to fire repeatedly with intermediate values.

**Android**
Programmatic smooth scrolls produced the same cascading
`PositionChanged` events as on Windows.
 
### Description of Change

**Windows**
Position Update: On item add, `ItemsView.Position` is explicitly set
based on `ItemsUpdatingScrollMode`, keeping current and previous
positions in sync.

Prevent Cascading Events: Added `_isInternalPositionUpdate`. For
collection changes, animations are disabled (animate = false) so
scrolling jumps directly, firing `PositionChanged` only once.

**Android**
Reused the `_isInternalPositionUpdate` logic. Disabled animations during
collection changes, ensuring a single clean position update without
duplicate events.
 
### Issues Fixed
Fixes dotnet#29529 
 
Tested the behaviour in the following platforms
- [x] Android
- [x] Windows
- [x] iOS
- [x] Mac

### Screenshots
| Before Issue Fix | After Issue Fix |
|------------------|-----------------|
| <img width="350" alt="withoutfix"
src="https://github.com/user-attachments/assets/d66f0352-a91f-4b85-bb9f-e0e54e55aa5f"
/> | <img width="350" alt="withfix"
src="https://github.com/user-attachments/assets/d120f268-6954-498d-aab0-42bc3745e296"
/> |

---------
…3953)

<!-- 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 iOS/MacCatalyst 26+, when the app theme changes (light ↔ dark), the
Switch's ThumbColor resets to white instead of keeping the custom color.

### Root Cause
On iOS/MacCatalyst 26+, UIKit resets the UISwitch's ThumbTintColor to
its default value during theme transitions.

### Description of Change
Register for trait collection changes to detect theme switches, then
re-apply the custom ThumbColor after UIKit completes its styling.

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

### Output  ScreenShot

|Before|After|
|--|--|
| <video
src="https://github.com/user-attachments/assets/aa595096-4f75-4d7e-b31e-f1f0acc24208"
>| <video
src="https://github.com/user-attachments/assets/386f9e6e-6df7-40c5-a3e7-59a0bde31df6">|
<!-- 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
WebView does not scroll when placed inside a ScrollView. The parent
ScrollView intercepts vertical touch gestures, preventing the WebView
from scrolling its internal content.

### Root Cause
The parent ScrollView was intercepting all touch events without checking
if the child WebView needed to scroll. This prevented the WebView from
receiving touch events and handling its own scrolling.

### Description of Change
Added touch event handling to prevent parent interception when WebView
scrolls. When touch begins or continues, WebView requests exclusive
control from parent. When touch ends or cancels, control returns to
parent. This enables WebView scrolling while preserving normal
parent-child interaction.
 
Validated the behavior in the following platforms
 
- [x] Android
- [x] Windows
- [x] iOS
- [x] Mac
 
### Issues Fixed
  
Fixes dotnet#32971  

### Output  ScreenShot

|Before|After|
|--|--|
| <video
src="https://github.com/user-attachments/assets/12b3ca6e-582d-4a50-9ea1-f49027f2d907"
>| <video
src="https://github.com/user-attachments/assets/2fbfd03c-4432-49e9-8b2d-6e7643f57487">|
… to null (dotnet#34741)

<!-- 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 iOS and macCatalyst, setting BackgroundColor = null on an Entry or
Editor at runtime does not restore the native default appearance. Once a
custom color is applied, it persists even after the property is cleared.

### Root Cause
The shared iOS background update logic only resets BackgroundColor =
null for LayoutView subclasses — all other UIView types silently return.
MauiTextField (Entry) and MauiTextView (Editor) are not LayoutView
subclasses, so the reset is skipped.

### Description of Change
Platform-specific MapBackground methods were added to
EntryHandler.iOS.cs and EditorHandler.iOS.cs. When
Background.IsNullOrEmpty(), they now explicitly set
platformView.BackgroundColor = null to restore native appearance.
 
Validated the behavior in the following platforms
 
- [x] Android
- [x] Windows
- [x] iOS
- [x] Mac
 
### Issues Fixed
  
Fixes dotnet#34611   

### Output  ScreenShot

|Before|After|
|--|--|
| <video
src="https://github.com/user-attachments/assets/5ca30c6d-c069-4c04-989b-4dae36584cb4"
>| <video
src="https://github.com/user-attachments/assets/ee9e2a2e-c210-47cc-9f85-2526780d398b">|

---------

Co-authored-by: Jakub Florkowski <42434498+kubaflo@users.noreply.github.com>
…t placed child to the Border control in iOS/ Mac platform (dotnet#33330)

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

Stacklayout is not rendered when clip is applied and StackLayout placed
child to the Border control in iOS/ Mac platform.
       
### Root Cause:

When the clip is applied to the StackLayout, its ContainerView
(WrapperView) is being inserted at index 0 to its parent control Border,
which places it below Border's background layer in the z-order. As a
result, the stacklayout is not visible in the view in iOS and Mac
platform.

### Description of Change:

The wrapper view is brought to the front of its parent’s subview stack
so it renders above the Border background in the Z order.

**Tested the behavior in the following platforms.**

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

### Reference:

N/A

### Issues Fixed:

Fixes  dotnet#33241    

### Screenshots
| Before  | After  |
|---------|--------|
| <img width="369" height="606" alt="image"
src="https://github.com/user-attachments/assets/0be8bc27-5de4-41ad-a41f-92581513ac55"
/> | <img width="369" height="606" alt="image"
src="https://github.com/user-attachments/assets/6a3590f6-2763-473a-aa91-ee1113e48ec3"
/> |
…cription is set (dotnet#33979)

<!-- 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
WinUI `TextBlock` (used by `Label`) automatically exposes its `Text` to
UI Automation. `ContentPanel` uses the default
`FrameworkElementAutomationPeer`, which exposes both the parent’s
`AutomationProperties.Name` and all child elements. Unlike Android
(`NoHideDescendants`) or iOS (`AccessibilityElementsHidden`), Windows
has no single property to hide descendants while keeping the parent
accessible. As a result, both Tab navigation and Browse mode announced
duplicate content.

### Description of Change
Implemented a custom `ContentPanelAutomationPeer` that overrides three
core UI Automation methods (GetAutomationControlTypeCore ,
`GetLocalizedControlTypeCore`, `GetChildrenCore`) to conditionally
modify behavior when Description is present.
 
When a Description exists, the control is exposed as
`AutomationControlType.Text` (enables browse mode navigation;
alternatives like Custom announce "custom" suffix, Group causes browse
mode to skip the element), the "text" announcement suffix is suppressed
via empty `GetLocalizedControlTypeCore()` return, and child elements are
hidden by returning null from `GetChildrenCore()` to prevent
duplication. When no Description is present, default behavior is
preserved with `AutomationControlType.Custom` and children remain
accessible.
The `HasDescription` helper property centralizes the non-empty
Description check across all three override methods, ensuring consistent
conditional logic following the MAUI `AutomationPeer patterns.

### Issues Fixed

Fixes dotnet#33373 

### Platforms Tested

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

### Screenshots
| Before Fix | After Fix |
|------------|-----------|
| <video width="350" alt="withoutfix"
src="https://github.com/user-attachments/assets/c5f8f114-fbeb-42d1-8601-e75dad57a1a7"
/> | <video width="350" alt="withfix"
src="https://github.com/user-attachments/assets/d2405df5-64f3-4cd9-8b38-40911ce4fbd6"
/> |

---------
…#33459)

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

### Description of Change

<img width="870" height="73" alt="image"
src="https://github.com/user-attachments/assets/e27cb50d-db93-431b-831c-24f7faedf9b1"
/>

Invalidation propagation can be quite consuming especially when
switching binding context and multiple properties change (i.e. having
multiple labels inside a layout, all of them changing the text).

Ti PR avoids useless propagations (same behavior `requestLayout`
intrinsically has on Android).
…g lifecycle transition (dotnet#34901)

<!-- 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 dotnet#34900

Replaces an opaque `NullReferenceException` with an informative
`InvalidOperationException` in `ContainerView` when the Android
`Context` is no longer available.

### Root Cause

`IMauiContext.Context` (the Android `Context`) is stored via a
`WeakReference` to the Activity. During lifecycle transitions (e.g.,
Activity being GC'd), this reference can become null. When
`NavigationRootManager.Connect()` calls
`view.ToContainerView(mauiContext)`, it creates a `ContainerView` whose
base `LinearLayout` constructor receives a null `Context`, causing an
NRE deep in the JNI interop layer.

### Fix

Added a null-coalescing throw expression in the `ContainerView`
constructor:

```csharp
public ContainerView(IMauiContext context)
    : base(context.Context ?? throw new InvalidOperationException(
        "Unable to create a ContainerView: the Android Context is no longer available. " +
        "This can occur when the Activity has been collected during a lifecycle transition."))
```

This provides a clear, actionable error message instead of the cryptic
NRE that pointed at `NavigationRootManager.Connect` line 60.

### Stack trace from the original crash

```
[AndroidRuntime] at Microsoft.Maui.Platform.NavigationRootManager.Connect
[AndroidRuntime] at Microsoft.Maui.Handlers.WindowHandler.CreateRootViewFromContent
[AndroidRuntime] at Microsoft.Maui.Handlers.WindowHandler.MapContent
```

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

<!--
!!!!!!! MAIN IS THE ONLY ACTIVE BRANCH. MAKE SURE THIS PR IS TARGETING
MAIN. !!!!!!!
-->
### Root Cause: 
CarouselView does not utilize the ItemSpacing property during layout
styling
### Description of Change
Added an item container style based on the horizontal or vertical
orientation of the CarouselView, and applied a corresponding style to
the ListViewBase
<!-- Enter description of the fix in this section -->

### Issues Fixed

<!-- Please make sure that there is a bug logged for the issue being
fixed. The bug should describe the problem and how to reproduce it. -->

Fixes dotnet#29772 
### Tested the behaviour in the following platforms

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

### Screenshot

| Before Issue Fix | After Issue Fix |
|----------|----------|
| <img
src="https://github.com/user-attachments/assets/4af88696-c5a6-4683-bceb-1933781368f3">
| <img
src="https://github.com/user-attachments/assets/f55c2b45-22c3-4ab2-aae5-c668adaf33e4">
|
<!--
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!
<!--
!!!!!!! MAIN IS THE ONLY ACTIVE BRANCH. MAKE SURE THIS PR IS TARGETING
MAIN. !!!!!!!
-->

### Issue Detail

RadioButtonGroup is not functioning correctly when RadioButton controls
are placed inside a ContentView in .NET 10.

### Root Cause

PR [dotnet#32640](dotnet#32604) (fixing dotnet#32466)
removed the coerceValue and Value = this initialiser from RadioButton,
so RadioButton.Value now defaults to null. This introduced two distinct
bugs in the ContentView+ControlTemplate scenario:

- AddRadioButton guard object.Equals(radioButton.Value,
this.SelectedValue) evaluates to object.Equals(null, null) == true,
causing every button added via a ControlTemplate to be auto-checked
immediately.
- When the layout is not yet attached to a Page (common during
ControlTemplate inflation), GetVisualRoot() returns null. The original
fallback radioButton.Parent resolves to the button's immediate parent
(Border), which only contains that single button — so other RadioButtons
in the same group are never found and never unchecked.

### Description of Change

- Updated the equality check in AddRadioButton to ensure comparison only
happens when SelectedValue is not null. This avoids unintended selection
during initialization.
- Improved the root resolution logic in UncheckOtherRadioButtonsInScope.
If no visual root is found, fallback to the RadioButtonGroupController’s
layout (group container).

### Issue Fixed
Fixes dotnet#34759 

### Screenshots

| Before  | After |
|---------|--------|
|  <video
src="https://github.com/user-attachments/assets/39e1e45d-dd04-42b7-bb85-aea83124b0cb">
|   <video
src="https://github.com/user-attachments/assets/252587b2-10ae-4eb9-968e-de114756aa0c"> 
|

---------

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

I tried to improve the access time on BindableProperty given they're
being accessed a lot during the application usage.
As we can see using Array + SIMD is actually performing better when the
number of BindableProperty set on a BindableObject is less than ~10,
though it becomes worse after that.
Array + SIMD also (obviously) allocates less memory.

Considering that SIMD may perform differently on different platforms I
felt it would be better to simply improve the current Dictionary based
implementation by leveraging:
- integer keys
- CollectionMarshal on GetOrAdd

### Benchmarks

| Strategy | PropertiesToSet | Mean | Error | StdDev | Gen0 | Gen1 |
Allocated |
|----------------------- |----------------
|------------:|---------:|---------:|-------:|-------:|----------:|
| Dictionary<BindableProperty> | 1 | 65.22 ns | 0.246 ns | 0.218 ns |
0.0889 | 0.0001 | 744 B |
| Array + SIMD | 1 | **52.93 ns** | 0.750 ns | 0.665 ns | 0.0678 | - |
568 B |
| Dictionary<int,> | 1 | 59.29 ns | 0.563 ns | 0.440 ns | 0.0889 |
0.0001 | 744 B |
| |
| Dictionary<BindableProperty> | 3 | 150.70 ns | 0.344 ns | 0.322 ns |
0.1500 | 0.0005 | 1256 B |
| Array + SIMD | 3 | **122.09 ns** | 0.163 ns | 0.127 ns | 0.1290 |
0.0002 | 1080 B |
| Dictionary<int,> | 3 | 133.50 ns | 0.231 ns | 0.180 ns | 0.1500 |
0.0005 | 1256 B |
| |
| Dictionary<BindableProperty> | 8 | 426.76 ns | 0.686 ns | 0.641 ns |
0.3662 | 0.0033 | 3064 B |
| Array + SIMD | 8 | **318.26 ns** | 0.438 ns | 0.409 ns | 0.3028 |
0.0024 | 2536 B |
| Dictionary<int,> | 8 | 338.98 ns | 2.381 ns | 1.988 ns | 0.3662 |
0.0038 | 3064 B |
| |
| Dictionary<BindableProperty> | 15 | 773.49 ns | 2.593 ns | 2.298 ns |
0.5798 | 0.0095 | 4856 B |
| Array + SIMD | 15 | 665.18 ns | 0.846 ns | 0.791 ns | 0.5531 | 0.0076
| 4632 B |
| Dictionary<int,> | 15 | **581.04 ns** | 0.431 ns | 0.360 ns | 0.5798 |
0.0095 | 4856 B |
| |
| Dictionary<BindableProperty> | 30 | 1,542.20 ns | 3.342 ns | 3.126 ns
| 1.1692 | 0.0381 | 9784 B |
| Array + SIMD | 30 | 1,419.83 ns | 1.301 ns | 1.154 ns | 1.0796 |
0.0324 | 9032 B |
| Dictionary<int,> | 30 | **1,165.86 ns** | 1.599 ns | 1.496 ns | 1.1692
| 0.0381 | 9784 B |
| |
| Dictionary<BindableProperty> | 50 | 2,648.69 ns | 3.759 ns | 2.935 ns
| 2.0828 | 0.1183 | 17448 B |
| Array + SIMD | 50 | 2,587.54 ns | 3.111 ns | 2.429 ns | 1.8196 |
0.0916 | 15224 B |
| Dictionary<int,> | 50 | **1,997.17 ns** | 2.174 ns | 2.033 ns | 2.0828
| 0.1183 | 17448 B |
…otnet#33891)

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

### Root Cause
When a password `Entry` field is empty and text is programmatically set,
the obfuscation logic calculates a negative insert position `(0 - 4 =
-4)`. String. `Insert()` doesn't accept negative indices, causing an
`ArgumentOutOfRangeException` crash in windows.

### Description of Change

Added `Math.Max(0, ...)` to clamp the insert position to a minimum of 0,
preventing negative **values.**

### Issues Fixed

Fixes dotnet#33334 

### Platforms Tested

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

### Screenshots
| Before Fix | After Fix |
|------------|-----------|
| <img width="350" alt="withoutfix"
src="https://github.com/user-attachments/assets/43993544-7700-4bff-a04c-d34b999ac962"
/> | <img width="350" alt="withfix"
src="https://github.com/user-attachments/assets/e7f12e20-1cd8-45e7-b07d-bfe7ebac6248"
/> |
…eaving custom state (dotnet#33346)

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

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

### Root Cause:

The issue occurs because platform handlers on iOS and Android do not
restore button properties to their default values when VisualState
setters are removed. In .NET MAUI, when a control transitions from a
custom VisualState back to the normal state, property values are set to
null to indicate that defaults should be restored. However, the platform
handlers interpreted these null values as instructions to take no
action, causing previously applied custom values to remain.

### Fix Description:

The fix involves updating button-specific extension methods to ensure
properties are correctly restored to their platform default values when
property values are null.

For iOS, the UpdateBackground extension method now explicitly handles
UIButton instances. When the background is null or empty,
RemoveBackgroundLayer() is called first to prevent stacking gradient
layers, and the background is reset to UIColor.Clear. This restores
proper transparency and aligns with the default iOS button appearance.
When a valid background is provided, the update continues through the
existing view background logic.

The UpdateTextColor method was also improved to restore the system
default text color correctly when TextColor is null. If the button is
attached to a UIWindow, the handler clears explicit overrides using
SetTitleColor(null, …) for the Normal, Highlighted, and Disabled states,
allowing iOS to fall back to its natural appearance-proxy behavior. It
then restores TintColor using window.TintColor rather than a hardcoded
system color. This ensures the app’s global tint is respected and
prevents clearing appearance settings during the initial render phase.

For Android, a MaterialButton-specific UpdateTextColor overload was
introduced in MauiMaterialButton. The default Material theme
ColorStateList is cached as DefaultTextColors during construction,
before any MAUI property mapping occurs. When TextColor becomes null,
the cached state list is restored directly, preserving all theme-defined
states (normal, disabled, etc.). This approach avoids creating temporary
controls and guarantees consistent restoration of the original Material
theme colors.

### Issues Fixed
Fixes dotnet#19690

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

### Output Screenshot
| Platform | Before Fix | After Fix |
|----------|----------|----------|
| Android | <video
src="https://github.com/user-attachments/assets/d96c404f-99b0-4dd2-bfdf-01adf629466e">
| <video
src="https://github.com/user-attachments/assets/e85313ef-3155-42d3-a1bc-c1b6ef8587b0">
|
|iOS | <video
src="https://github.com/user-attachments/assets/416724b2-f8fe-4146-be56-12c3f6808693">
| <video
src="https://github.com/user-attachments/assets/962ce83a-de7f-4ca0-8339-c8ce88e99288">
|
…tating the device with dialog open (dotnet#31910)

<!-- 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, when opening a TimePicker and then rotating the device,
the picker dialog doesn’t seem to redraw itself to match the new screen
dimensions.

### Root Cause of the issue

- TimePicker lacks orientation change detection entirely. When the
device rotates while the dialog is open, TimePicker has no mechanism to
dismiss and re-show the dialog with updated layout, unlike DatePicker
which detects orientation changes and refreshes the dialog display.

### Description of Change

- Added ConnectHandler and related event subscriptions
(ViewAttachedToWindow/ViewDetachedFromWindow) to manage display info
changes and cleanup when the view is attached or detached. This helps
ensure the time picker dialog responds to device orientation changes and
releases resources properly.

- Implemented OnMainDisplayInfoChanged to dismiss and recreate the time
picker dialog with the current time when the device orientation changes,
preserving user selection progress.

### Issues Fixed

<!-- Please make sure that there is a bug logged for the issue being
fixed. The bug should describe the problem and how to reproduce it. -->

Fixes dotnet#31658 

### Reference

-
[DatePickerHandler](https://github.com/dotnet/maui/blob/main/src/Core/src/Handlers/DatePicker/DatePickerHandler.Android.cs)

### Tested the behaviour in the following platforms

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

### Output
| Before | After |
|----------|----------|
| <video
src="https://github.com/user-attachments/assets/55091d04-0aa3-4794-aab8-fb5dfc71624e">
| <video
src="https://github.com/user-attachments/assets/56c01750-00a1-4c63-8788-ca6c35d7dbd5">
|

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

### Issue details

Changing the Location property on a Microsoft.Maui.Controls.Maps.Pin
does not change the location of the Pin on the map.

### Root cause
`MapPinHandler.Android.cs` was only updating the `MarkerOptions` object
when the Location property changed
The actual `Marker` object already added to the map was not being
updated, causing the pin to stay in its original location

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

### Description of change

* **Improved marker association logic**: Updated the `AddPins` method in
`MapHandler` to store marker references in the `MapPinHandler` for
future property updates, ensuring tighter integration between pins and
markers.

Validated the behaviour in the following platforms

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

### Issues Fixed

<!-- Please make sure that there is a bug logged for the issue being
fixed. The bug should describe the problem and how to reproduce it. -->

Fixes dotnet#12916

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

### Output
| Before| After|
|--|--|
| <video
src="https://github.com/user-attachments/assets/83bedab7-846b-41c1-872e-b6e0d0cd81a4">
| <video
src="https://github.com/user-attachments/assets/fd86fd38-6619-4cf9-999a-de9d05b21e17">
|

---------

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

## Description

Fixes dotnet#19866

On iOS, tapping the status bar should scroll the topmost `UIScrollView`
to the top. This was not working for `CollectionView`, particularly when
hosted inside a `Shell`.

## Root Cause

iOS disables its scroll-to-top behavior when **multiple** `UIScrollView`
instances in the view hierarchy have `scrollsToTop = true`. The Shell
flyout's internal `AccessibilityNeutralTableView` (a `UITableView`
subclass) defaults `scrollsToTop` to `true`, conflicting with the
`CollectionView`'s `UICollectionView`.

## Fix

Two changes:

1. **`ItemsViewController2.ViewDidLoad()`** — Explicitly set
`CollectionView.ScrollsToTop = true` to opt in to scroll-to-top behavior
2. **`ShellTableViewController.AccessibilityNeutralTableView`** — Set
`ScrollsToTop = false` on Shell's flyout table view to prevent the
multi-scroll-view conflict

This follows the existing codebase pattern where `ScrollsToTop` is
explicitly managed (e.g., `ShellSectionRootHeader` and
`ContextActionCell` both set it to `false`).

## Validation

Verified with a Sandbox app using a grouped `CollectionView` inside a
`Shell` with `TabBar`:
- **Without fix**: Diagnostic dump shows two scroll views with
`scrollsToTop=True` → status bar tap does nothing
- **With fix**: Only `MauiCollectionView` has `scrollsToTop=True` →
status bar tap scrolls to top correctly

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ht space above the tab bar even if the page title is empty (dotnet#30382)

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

### Root cause

The root cause of this issue is that the Windows Shell implementation
always reserves space for the header area when
Shell.FlyoutBehavior="Flyout" is set, even when pages have no title or
header content. The NavigationView control allocates a fixed height
above the tab bar for the header region without checking if the current
page actually needs this space. This results in an unwanted gap between
the flyout toggle area and the tab bar when navigating to pages with
empty titles, creating visual inconsistency compared to
FlyoutBehavior="Disabled" mode where no header space is reserved for
empty content.

### Description of Issue Fix

The fix involves adding header visibility logic to the Windows Shell
implementation to remove unwanted space above the tab bar in flyout
mode. The implementation introduces three key methods in
RootNavigationView.cs: UpdateHeaderVisibility() to evaluate header
visibility requirements, IsHeaderContentEmpty() to determine if the
toolbar contains no title or title view when in flyout mode, and
CollapseEmptyHeader() to properly hide empty headers. The toolbar
property setter now calls UpdateHeaderVisibility() when the toolbar
changes, and ShellView.cs triggers header visibility updates during tab
navigation. This ensures empty headers don't occupy unnecessary space in
flyout mode while maintaining normal behavior when headers contain
content, resolving the spacing issue above the tab bar when switching
between pages in Shell applications on Windows.

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

### Issues Fixed

<!-- Please make sure that there is a bug logged for the issue being
fixed. The bug should describe the problem and how to reproduce it. -->

Fixes dotnet#30254

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

### Resaved Test snapshots

Resaved the below test snapshot because the ContentPage did not have a
title. So, resaved the test snapshot based on the fix.

1. ShellFlowDirectionUpdate
2. VerifyFlyoutBackgroundColor
3. VerifyHamburgerIcon
4. Issue23834FlyoutMisbehavior

### Output

| Before Issue Fix | After Issue Fix |
|----------|----------|
| <video width="270" height="600"
src="https://github.com/user-attachments/assets/ca496f96-2500-429f-8720-a8adb7925fce">
| <video width="270" height="600"
src="https://github.com/user-attachments/assets/1f0dedd2-4808-46bf-98cf-843ca70294ef">
|
…forms (dotnet#30369)

<!-- 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 Detail
The FlowDirection property is not respected by the TimePicker control.
Setting FlowDirection="RightToLeft" has no visual effect on the control
across any platform.

### Root Cause
iOS: The MapFlowDirection method incorrectly used the concrete
TimePickerHandler type instead of the ITimePickerHandler interface. This
bypassed platform-specific logic and defaulted to
ViewHandler.MapFlowDirection, which lacked the necessary text alignment
handling.

Android: The FlowDirection property was not correctly mapped to the
native implementation, resulting in incorrect or missing text alignment
updates.

### Description of Change
iOS: Updated the MapFlowDirection method to use the correct handler
interface. Implemented alignment logic in TimePickerExtensions.cs using
EffectiveFlowDirection to properly align text for both RTL and LTR
layouts.

Android: Implemented a platform-specific MapFlowDirection method in
TimePickerHandler.Android.cs:
For 12-hour format, UpdateTextAlignment is applied to align localized
"AM/PM" text based on FlowDirection.
For 24-hour format, alignment is handled via UpdateFlowDirection, which
sets LayoutDirection and TextDirection to align purely numeric content

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

### Issues Fixed:
Fixes dotnet#30192 

### Screenshots

| Before  | After |
|---------|--------|
|  <img
src="https://github.com/user-attachments/assets/37c4a442-1011-4c23-bc2e-8743bc11adc7">
|  <img
src="https://github.com/user-attachments/assets/91b69287-b8eb-4d40-b8a1-0734ef1f89db"> 
|
@kubaflo
Copy link
Copy Markdown
Contributor Author

kubaflo commented Apr 13, 2026

/azp run maui-pr-uitests , maui-pr-devicetests

@azure-pipelines
Copy link
Copy Markdown

Azure Pipelines successfully started running 2 pipeline(s).

kubaflo and others added 3 commits April 13, 2026 18:16
…for HTML labels (dotnet#34934)

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

Fixes the
`CharacterSpacingWithLineHeightWithTextDecorationsWorksCorrectly` device
test regression introduced by PR dotnet#31202.

PR dotnet#31202 changed the Label mapper conditions from `!IsPlainText(label)`
to `label.HasFormattedTextSpans` for `MapLineHeight`,
`MapTextDecorations`, and `MapCharacterSpacing`. This correctly enables
these properties to be applied to HTML labels (`TextType.Html`), since
`HasFormattedTextSpans` only returns true for `FormattedText` with spans
— not for HTML.

However, the test expectations for `label4` (an HTML label with
`CharacterSpacing=5`, `LineHeight=1.5`, `TextDecorations=Underline`)
were not updated and still expected `(0, 0, None)`.

**Fix:** Update expected values for label4 from `(0, 0, None)` to `(5,
1.5, Underline)` to match the new behavior.

### Issues Fixed

Fixes CharacterSpacingWithLineHeightWithTextDecorationsWorksCorrectly
device test regression from dotnet#31202

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…dotnet#26217)

### Root cause

The issue arises because the OnNavigationViewSizeChanged method fails to
properly reset the layout measurements before arranging the
NavigationView. As a result, the NavigationView does not correctly
update its layout in response to size changes, causing misalignment or
rendering issues in the ScrollView.

### Description of Issue Fix

The fix involves updating the OnNavigationViewSizeChanged() method to
include a call to InvalidateMeasure() before arranging the
NavigationView. This ensures that the layout is accurately recalculated,
allowing the ScrollView and other elements within the TabbedPage to be
properly measured and arranged during the subsequent layout cycle. This
effectively resolves alignment and rendering issues.

Additionally, the Arrange() call is retained within the SizeChanged
handler to prevent test failures, specifically avoiding timeout issues
observed in the ChangingToNewMauiContextDoesntCrash test. This
combination ensures stable layout behavior while resolving the clipping
and scrolling issues that occur after window resizing.

### Why Tests were not added:

**Regarding the test case:** The issue only occurs when resizing the
window, so it is not possible to add a test case for the window resizing
behavior.

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

### Issues Fixed

<!-- Please make sure that there is a bug logged for the issue being
fixed. The bug should describe the problem and how to reproduce it. -->

Fixes dotnet#26103
Fixes dotnet#11402
Fixes dotnet#20028

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

### Resaved Test snapshots
 
Resaved the below-mentioned test snapshot because elements in the
TabbedPage were not properly aligned before the fix. The layout changes
in OnNavigationViewSizeChanged (adding Arrange() after
InvalidateMeasure()) now ensure proper element alignment within the
TabbedPage.
 
1. DefaultSelectedTabTextColorShouldApplyProperly
2. FontImageSourceColorShouldApplyOnTabIcon
3. VerifyTabbedPageMenuItemTextColor
4. DynamicFontImageSourceColorShouldApplyOnTabIcon
5. Issue1323Test
6. TabBarIconsShouldAutoscaleTabbedPage

### Output

| Before Issue Fix | After Issue Fix |
|----------|----------|
| <video width="270" height="600"
src="https://github.com/user-attachments/assets/67d34cc9-323c-4a8d-afc6-dbcceb558ee5">
| <video width="270" height="600"
src="https://github.com/user-attachments/assets/0ddb5ce5-300a-4088-98f4-f9a3981d6951">
|

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

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

### Root Cause:
The UpdateCharacterSpacing method didn't apply spacing to the individual
text blocks in TimePicker

### Description of Change 
Enhanced the UpdateCharacterSpacing method to apply CharacterSpacing to
individual text blocks (HourTextBlock, MinuteTextBlock, and
PeriodTextBlock) within the TimePicker. This ensures the property works
correctly even when the control is loaded asynchronously.

<!-- Enter description of the fix in this section -->

### Issues Fixed

<!-- Please make sure that there is a bug logged for the issue being
fixed. The bug should describe the problem and how to reproduce it. -->

Fixes dotnet#30199 

### Tested the behaviour in the following platforms

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

### Screenshot

| Before Issue Fix | After Issue Fix |
|----------|----------|
| <img width="1217" height="915" alt="beforeFix30199"
src="https://github.com/user-attachments/assets/0b955bf7-5e75-48d3-8455-3fe0cf2c0c5b">
| <img width="1261" height="946" alt="Screenshot 2025-07-10 155258"
src="https://github.com/user-attachments/assets/5d2d8a34-fb01-4c58-9fef-be3ad10b7cb0">
|

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

---------
…tnet#34383)

<!-- Please let the below note in for people that find this PR -->
> [!NOTE]
> Are you waiting for the changes in this PR to be merged?
> It would be very helpful if you could [test the resulting
artifacts](https://github.com/dotnet/maui/wiki/Testing-PR-Builds) from
this PR and let us know in a comment if this change resolves your issue.
Thank you!
<!--
!!!!!!! MAIN IS THE ONLY ACTIVE BRANCH. MAKE SURE THIS PR IS TARGETING
MAIN. !!!!!!!
-->
### Root Cause:
HTML text is applied asynchronously in .NET MAUI on iOS, causing the
CollectionView cell to render first and then re-measure after the HTML
is applied, which leads to visible resizing (scroll jitter).
### Description of Change
Improvements to HTML text handling in CollectionView:

* Updated `UpdateText` method in `LabelExtensions.cs` to check if the
`UILabel` is inside a CV2 cell using the new
`IsPlatformLabelInsideCV2Cell` method, allowing synchronous HTML text
updates when safe and avoiding unnecessary layout passes.
* Added the `IsPlatformLabelInsideCV2Cell` helper method to walk the
UIKit superview chain and detect CV2 cells, improving reliability and
preventing crashes in CV1 layouts.
* Added a reference to `Microsoft.Maui.Controls.Handlers.Items2` to
support the new CV2 cell detection logic.
<!-- Enter description of the fix in this section -->
### Why Tests were not added:
This bug occurs only during live scrolling of CollectionView cells on
iOS when a Label with TextType="Html" is rendered. The issue depends on
the precise timing of asynchronous HTML text application, which triggers
a second layout pass while cells are visible, causing scroll jitter.
Automated tests cannot reliably reproduce this because no framework can
simulate real-time scrolling and layout timing at the native speed
### Issues Fixed

<!-- Please make sure that there is a bug logged for the issue being
fixed. The bug should describe the problem and how to reproduce it. -->

Fixes dotnet#33065 

### 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/1045cbe3-e869-4e08-9562-8032dd9cea88">
| <video
src="https://github.com/user-attachments/assets/0c7b3bbb-4917-482f-ac7e-05e343de3ae0">
|


<!--
Are you targeting main? All PRs should target the main branch unless
otherwise noted.
-->
@kubaflo kubaflo closed this Apr 13, 2026
@kubaflo kubaflo reopened this Apr 13, 2026
@kubaflo kubaflo changed the base branch from inflight/current to main April 13, 2026 21:37
Copilot AI review requested due to automatic review settings April 13, 2026 21:38
@kubaflo kubaflo force-pushed the fix/flyout-header-scroll-test branch from ab9d59b to 096f301 Compare April 13, 2026 21:38
@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://github.com/dotnet/maui/main/eng/scripts/get-maui-pr.sh | bash -s -- 34932

Or

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

@kubaflo kubaflo changed the base branch from main to inflight/current April 13, 2026 21:41
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Note

Copilot was unable to run its full agentic suite in this review.

Fixes an iOS Shell flyout header measurement regression affecting FlyoutHeaderScroll-style scenarios, and additionally includes a broad set of framework/runtime, handler, SourceGen, test, and sample updates across multiple platforms.

Changes:

  • Adjusts iOS Shell flyout header measurement invalidation to cooperate with scroll-driven header resizing.
  • Introduces an x:Code XAML SourceGen pipeline (parsing + code emission) gated behind preview features and x:Class.
  • Adds/updates numerous cross-platform fixes and test coverage (VSM specificity, RadioButton semantics/grouping, ItemsView selection/recycling, WebView2 disposal hardening, etc.), plus some sample/docs/build changes.

Reviewed changes

Copilot reviewed 1 out of 1 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
src/Controls/tests/TestCases.HostApp/Issues/Issue21983.xaml.cs Adds HostApp issue page code-behind for Shape stroke gradient repro.
src/Controls/tests/TestCases.HostApp/Issues/Issue21983.xaml Adds XAML repro for gradient Stroke on shapes.
src/Controls/tests/TestCases.HostApp/Issues/Issue20991.cs Adds HostApp repro for binding support with IDrawable.
src/Controls/tests/TestCases.HostApp/Issues/Issue20348.cs Adds HostApp repro for Android SearchBar text transfer after back nav.
src/Controls/tests/TestCases.HostApp/Issues/Issue20062.cs Adds HostApp repro for CollectionView selected VSM on Android.
src/Controls/tests/TestCases.HostApp/Issues/Issue19866.cs Adds HostApp repro for iOS status-bar tap scroll-to-top behavior.
src/Controls/tests/TestCases.HostApp/Issues/Issue19690.cs Adds HostApp repro for Button VisualStates issues.
src/Controls/tests/TestCases.HostApp/Issues/Issue19168.cs Adds HostApp repro for iOS/macOS Picker dismissal behavior.
src/Controls/tests/TestCases.HostApp/Issues/Issue18657.cs Adds HostApp repro for CollectionView EmptyView removal on UWP/Windows.
src/Controls/tests/TestCases.HostApp/Issues/Issue18200.cs Adds HostApp repro for Windows FlyoutPage collapse style changes.
src/Controls/tests/TestCases.HostApp/Issues/Issue11404.cs Adds HostApp repro for Shape/Line coordinate/bounds correctness.
src/Controls/tests/TestCases.HostApp/Issues/Issue10987.cs Adds HostApp repro for Editor horizontal alignment behavior.
src/Controls/tests/TestCases.HostApp/FeatureMatrix/Slider/SliderViewModal.cs Adds VM properties for Slider ValueChanged event reporting.
src/Controls/tests/TestCases.HostApp/FeatureMatrix/Slider/SliderControlPage.xaml.cs Hooks ValueChanged and resets status when navigating to options.
src/Controls/tests/TestCases.HostApp/FeatureMatrix/ScrollView/ScrollViewViewModel.cs Adds VM properties to surface ScrollToRequested details.
src/Controls/tests/TestCases.HostApp/FeatureMatrix/ScrollView/ScrollViewControlPage.xaml.cs Records ScrollToRequested event args into the VM.
src/Controls/tests/TestCases.HostApp/FeatureMatrix/ScrollView/ScrollViewControlPage.xaml Adds UI to display ScrollToRequested state/args.
src/Controls/tests/TestCases.HostApp/Controls.TestCases.HostApp.csproj Adds a logical name mapping for challenge images.
src/Controls/tests/DeviceTests/Resources/Raw/HybridTestRoot/index.html Adds JS helpers for JSON-string argument scenarios.
src/Controls/tests/DeviceTests/Elements/Shell/ShellTests.iOS.cs Adds device test ensuring flyout table view ScrollsToTop is disabled.
src/Controls/tests/DeviceTests/Elements/RadioButton/RadioButtonTests.cs Adds device tests for templated RadioButton semantics description behavior.
src/Controls/tests/DeviceTests/Elements/Label/LabelTests.iOS.cs Updates label spacing/lineheight data for a test case.
src/Controls/tests/DeviceTests/Elements/HybridWebView/HybridWebViewTests_SetInvokeJavaScriptTarget.cs Updates expected JSON escaping in test data.
src/Controls/tests/DeviceTests/Elements/HybridWebView/HybridWebViewTests_EvaluateJavaScriptAsync.cs Splits EvaluateJavaScript tests by parameter type and quoting cases.
src/Controls/tests/DeviceTests/Elements/HybridWebView/HybridWebViewTestsBase.cs Adds a 15s timeout wrapper around test execution.
src/Controls/tests/DeviceTests/Elements/Entry/EntryTests.cs Adds device tests for long text and MaxLength/Text ordering.
src/Controls/tests/DeviceTests/Elements/Entry/EntryTests.Windows.cs Adds Windows regression test for password text setting from empty.
src/Controls/tests/DeviceTests/Elements/Editor/EditorTests.iOS.cs Adds iOS test for Editor placeholder horizontal text alignment.
src/Controls/tests/DeviceTests/Elements/ContentView/ContentViewTests.Windows.cs Adds Windows tests around Narrator/automation peer behavior.
src/Controls/tests/DeviceTests/Elements/CollectionView/CollectionViewTests.iOS.cs Adds iOS test for ScrollsToTop and a CarouselView indicator visibility test.
src/Controls/tests/DeviceTests/Elements/CarouselView/CarouselViewTests.cs Removes Android exclusion for a disconnect regression test.
src/Controls/tests/DeviceTests/Elements/CarouselView/CarouselViewTests.Android.cs Adds Android accessibility description test for IndicatorView dots.
src/Controls/tests/Core.UnitTests/VisualStateManagerTests.cs Adds VSM specificity regression tests (#34363, custom-state behavior).
src/Controls/tests/Core.UnitTests/SolidColorBrushTests.cs Adds Brush.HasTransparency tests for SolidColorBrush.
src/Controls/tests/Core.UnitTests/RadioButtonTests.cs Adds RadioButton group tests for ControlTemplate/ContentView scenarios.
src/Controls/tests/Core.UnitTests/RadialGradientBrushTests.cs Adds Brush.HasTransparency tests for RadialGradientBrush.
src/Controls/tests/Core.UnitTests/LinearGradientBrushTests.cs Adds Brush.HasTransparency tests for LinearGradientBrush.
src/Controls/tests/Core.UnitTests/BindablePropertyUnitTests.cs Adds unit test for CollectionView header unwrapping of OnPlatform.
src/Controls/tests/Core.UnitTests/AlertManagerTests.cs Updates tests for new alert manager abstraction and DI resolution.
src/Controls/src/Xaml/XmlName.cs Adds x:Code XmlName constant.
src/Controls/src/SourceGen/XamlGenerator.cs Adds x:Code extraction/emission pipeline and feeds it into compilation.
src/Controls/src/SourceGen/XCodeCodeWriter.cs New code writer for generating partial class members from x:Code blocks.
src/Controls/src/SourceGen/Visitors/SetPropertiesVisitor.cs Ignores x:Code in property visitor handling.
src/Controls/src/SourceGen/TrackingNames.cs Adds tracking names for the x:Code pipeline.
src/Controls/src/SourceGen/InitializeComponentCodeWriter.cs Strips x:Code elements from node tree before IC generation.
src/Controls/src/SourceGen/GeneratorHelpers.cs Adds x:Code extraction logic and helper to detect x:Code elements.
src/Controls/src/SourceGen/Descriptors.cs Adds diagnostics for x:Code constraints (child-of-root, requires x:Class).
src/Controls/src/SourceGen/AnalyzerReleases.Unshipped.md Documents new MAUIX2015/MAUIX2016 diagnostics.
src/Controls/src/Core/Window/Window.cs Makes Window alert manager DI-resolvable and manages subscribe lifecycle.
src/Controls/src/Core/VisualStateManager.cs Adjusts VSM specificity application/unapplication for system-driven states.
src/Controls/src/Core/VisualElement/VisualElement.cs Exposes internal explicit IsEnabled state and adjusts Loaded wiring.
src/Controls/src/Core/VisualElement/VisualElement.Platform.cs Handles Loaded/Unloaded when hosted in native container via ToPlatform.
src/Controls/src/Core/TabbedPage/TabbedPage.Windows.cs Adjusts size-changed handling to invalidate measure and arrange properly.
src/Controls/src/Core/Style.cs Unapplies BasedOn style before applying as base style.
src/Controls/src/Core/Shell/ShellItem.cs Uses ShellContent as effective value source for TabBar visibility if set.
src/Controls/src/Core/Shell/ShellElementCollection.cs Propagates TabBarIsVisible changes from ShellContent to displayed page.
src/Controls/src/Core/Shapes/Shape.cs Improves Stretch.None translation logic to fit within view bounds.
src/Controls/src/Core/SetterSpecificity.cs Adds helper to promote implicit VSM specificity to full VSM priority.
src/Controls/src/Core/Setter.cs Improves TargetName resolution across namescope boundaries (templates).
src/Controls/src/Core/RadioButton/RadioButtonGroupController.cs Fixes auto-check behavior when SelectedValue is null.
src/Controls/src/Core/RadioButton/RadioButtonGroup.cs Improves group root selection when no Page ancestor exists yet.
src/Controls/src/Core/RadioButton/RadioButton.cs Improves semantics description extraction from templated content.
src/Controls/src/Core/PublicAPI/netstandard/PublicAPI.Unshipped.txt Adds new alert manager interfaces to public API list.
src/Controls/src/Core/PublicAPI/net/PublicAPI.Unshipped.txt Adds new alert manager interfaces to public API list.
src/Controls/src/Core/PublicAPI/net-windows/PublicAPI.Unshipped.txt Adds alert manager interfaces + a CarouselViewHandler signature entry.
src/Controls/src/Core/PublicAPI/net-tizen/PublicAPI.Unshipped.txt Adds new alert manager interfaces to public API list.
src/Controls/src/Core/PublicAPI/net-maccatalyst/PublicAPI.Unshipped.txt Adds new alert manager interfaces + other iOS/mac changes to API list.
src/Controls/src/Core/PublicAPI/net-ios/PublicAPI.Unshipped.txt Adds new alert manager interfaces + other iOS changes to API list.
src/Controls/src/Core/PublicAPI/net-android/PublicAPI.Unshipped.txt Adds alert manager interfaces + other Android changes to API list.
src/Controls/src/Core/PlatformConfiguration/WindowsSpecific/FlyoutPage.cs Adds property-changed callback to update handler for CollapseStyle.
src/Controls/src/Core/Platform/iOS/Extensions/LabelExtensions.cs Avoids async HTML update in CV2; keeps async dispatch for CV1.
src/Controls/src/Core/Platform/iOS/ControlsModalWrapper.cs Uses brush transparency (Background) to select modal presentation style.
src/Controls/src/Core/Platform/Windows/Extensions/AutoSuggestBoxExtensions.cs Adds helpers to map SearchHandler properties to AutoSuggestBox.
src/Controls/src/Core/Platform/Windows/CollectionView/ItemContentControl.cs Fixes logical tree reattachment and selection equality checks.
src/Controls/src/Core/Platform/ModalNavigationManager/ModalNavigationManager.Android.cs Uses DismissNow for batch modal pops; forwards modal touch events.
src/Controls/src/Core/Platform/GestureManager/GesturePlatformManager.Android.cs Adds RecyclerView touch listener path and lifecycle cleanup.
src/Controls/src/Core/Platform/AlertManager/IAlertManagerSubscription.cs New public interface for alert subscription backend.
src/Controls/src/Core/Platform/AlertManager/IAlertManager.cs New public interface for alert manager abstraction.
src/Controls/src/Core/Platform/AlertManager/AlertManager.cs Makes AlertManager implement IAlertManager; removes nested subscription interface.
src/Controls/src/Core/Label/Label.iOS.cs Updates iOS formatting map to include line height/decorations/spacing.
src/Controls/src/Core/Label/Label.cs Improves measure invalidation for empty/non-empty transitions.
src/Controls/src/Core/Label/Label.Mapper.cs Avoids applying certain maps when formatted spans exist.
src/Controls/src/Core/Items/MarshalingObservableCollection.cs Uses weak proxy and adds explicit Dispose to unsubscribe.
src/Controls/src/Core/Handlers/Shell/Windows/ShellView.cs Updates header visibility on tab selection changes.
src/Controls/src/Core/Handlers/Shell/ShellItemHandler.Windows.cs Improves SearchHandler ↔ AutoSuggestBox mapping and cancel button coloring.
src/Controls/src/Core/Handlers/Items2/iOS/StructuredItemsViewController2.cs Updates flow direction for default header/footer cells.
src/Controls/src/Core/Handlers/Items2/iOS/ReorderableItemsViewDelegator2.cs Consolidates grouped-move logic into shared helper.
src/Controls/src/Core/Handlers/Items2/iOS/ItemsViewController2.cs Enables ScrollsToTop and refines flow direction update logic.
src/Controls/src/Core/Handlers/Items2/CarouselViewHandler2.iOS.cs Fixes ScrollToRequested cast and updates swipe enabled mapping.
src/Controls/src/Core/Handlers/Items/iOS/ReorderableItemsViewExtensions.cs New shared helper for grouped reorder move target index calculation.
src/Controls/src/Core/Handlers/Items/iOS/ReorderableItemsViewDelegator.cs Uses shared grouped-move helper for CV1.
src/Controls/src/Core/Handlers/Items/iOS/MauiCollectionView.cs Intercepts AddSubview to apply swipe enabled to internal scrollers.
src/Controls/src/Core/Handlers/Items/iOS/CarouselViewController.cs Adds guards for invalid indexes and out-of-range scroll targets.
src/Controls/src/Core/Handlers/Items/StructuredItemsViewHandler.Windows.cs Cleans up header/footer realized views when removed.
src/Controls/src/Core/Handlers/Items/SelectableItemsViewHandler.Windows.cs Uses value equality for selection comparisons.
src/Controls/src/Core/Handlers/Items/ItemsViewHandler.Windows.cs Refactors EmptyView removal and template realization behavior.
src/Controls/src/Core/Handlers/Items/Android/TemplatedItemViewHolder.cs Re-realizes content when handler was disconnected during recycle.
src/Controls/src/Core/Handlers/Items/Android/SimpleItemTouchHelperCallback.cs Changes reorder move filtering logic.
src/Controls/src/Core/Handlers/Items/Android/MauiRecyclerView.cs Respects inherited IsEnabled differently via explicit enabled state.
src/Controls/src/Core/Handlers/Items/Android/ItemsSources/ObservableItemsSource.cs Optionally disposes the wrapped items source on disposal.
src/Controls/src/Core/Handlers/Items/Android/ItemsSources/ItemsSourceFactory.cs Enables disposal for MarshalingObservableCollection wrapper.
src/Controls/src/Core/Handlers/Items/Android/Adapters/SelectableItemsViewAdapter.cs Forces platform selection sync for Single mode after click.
src/Controls/src/Core/Handlers/Items/Android/Adapters/ReorderableItemsViewAdapter.cs Adds bounds checks for source/target indices in item moves.
src/Controls/src/Core/GraphicsView/GraphicsView.cs Propagates binding context to bindable Drawable and hooks propertyChanged.
src/Controls/src/Core/FlyoutPage/FlyoutPage.Mapper.cs Adds Windows mapping for collapse style; preserves iOS mappings.
src/Controls/src/Core/Compatibility/Handlers/Shell/iOS/SlideFlyoutTransition.cs Adjusts RTL locked flyout framing logic.
src/Controls/src/Core/Compatibility/Handlers/Shell/iOS/ShellTableViewSource.cs Resolves FlowDirection for disconnected Shell menu cell view trees.
src/Controls/src/Core/Compatibility/Handlers/Shell/iOS/ShellTableViewController.cs Disables ScrollsToTop for flyout table view.
src/Controls/src/Core/Compatibility/Handlers/Shell/iOS/ShellSectionRootRenderer.cs Resolves MatchParent flow direction for tracked page.
src/Controls/src/Core/Compatibility/Handlers/Shell/iOS/ShellSectionRenderer.cs Updates tab bar flow direction in shell section renderer.
src/Controls/src/Core/Compatibility/Handlers/Shell/iOS/ShellPageRendererTracker.cs Fixes page visibility state when swapping pages before animation.
src/Controls/src/Core/Compatibility/Handlers/Shell/iOS/ShellFlyoutHeaderContainer.cs Fixes header measurement invalidation to avoid fighting layout manager.
src/Controls/src/Core/Compatibility/Handlers/Shell/iOS/ShellFlyoutContentRenderer.cs Updates flow direction for header/footer and resolves header MatchParent flow.
src/Controls/src/Core/Compatibility/Handlers/Shell/Android/ShellToolbarTracker.cs Adjusts back button icon logic and clears search UI state on handler swap.
src/Controls/src/Core/Compatibility/Handlers/Shell/Android/ShellItemRenderer.cs Requests insets after tab bar gone→visible transition to fix padding.
src/Controls/src/Core/Brush/Brush.cs Adds internal HasTransparency helper for brush transparency detection.
src/Controls/src/Core/BindableProperty.cs Adds InternalId and adjusts conversion logic for wrapped values.
src/Controls/src/Core/BindableObject.cs Switches property context storage to int key map and uses CollectionsMarshal.
src/Controls/samples/Controls.Sample/Pages/Controls/MapsGalleries/MapPinsGallery.xaml.cs Adds move-pin support and map recentering in sample.
src/Controls/samples/Controls.Sample/Pages/Controls/MapsGalleries/MapPinsGallery.xaml Adds Move Pin button to maps sample.
src/Controls/Maps/src/AppHostBuilderExtensions.cs Updates Windows maps documentation and removes Windows NotImplementedException path.
src/BlazorWebView/src/Wpf/BlazorWebView.cs Marks WebView2 manager as disposing before async cleanup.
src/BlazorWebView/src/SharedSource/WebView2WebViewManager.cs Guards PostWebMessage against disposal/races; adds disposal flag and API.
src/BlazorWebView/src/Maui/build/Microsoft.AspNetCore.Components.WebView.Maui.targets Filters compressed assets when CompressionEnabled=false.
src/AI/src/Essentials.AI/PublicAPI/net-macos/PublicAPI.Unshipped.txt Moves AI API entries from Unshipped to Shipped.
src/AI/src/Essentials.AI/PublicAPI/net-macos/PublicAPI.Shipped.txt Adds AI-shipped API entries with MAUIAI0001 prefix.
src/AI/src/Essentials.AI/PublicAPI/net-maccatalyst/PublicAPI.Unshipped.txt Moves AI API entries from Unshipped to Shipped.
src/AI/src/Essentials.AI/PublicAPI/net-maccatalyst/PublicAPI.Shipped.txt Adds AI-shipped API entries with MAUIAI0001 prefix.
src/AI/src/Essentials.AI/PublicAPI/net-ios/PublicAPI.Unshipped.txt Moves AI API entries from Unshipped to Shipped.
src/AI/src/Essentials.AI/PublicAPI/net-ios/PublicAPI.Shipped.txt Adds AI-shipped API entries with MAUIAI0001 prefix.
eng/pipelines/ci-official.yml Skips simulator setup in official CI pipeline parameters.
eng/Versions.props Bumps MonoApiTools MSBuild tasks package version.


namespace Microsoft.Maui.Controls.Platform.Compatibility
{
internal class ShellFlyoutHeaderContainer : UIContainerView, IPlatformMeasureInvalidationController
Copy link

Copilot AI Apr 13, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The PR title/description focuses on an iOS FlyoutHeaderScroll device test regression, but this PR includes a very large set of unrelated framework changes (SourceGen x:Code, AlertManager DI/public APIs, Items handlers across platforms, Maps, BlazorWebView, build pipeline, etc.). This makes review/risk management difficult and complicates servicing. Consider splitting this into focused PRs (or updating the PR description and title to reflect the full scope) so the Shell flyout regression fix can be evaluated and potentially cherry-picked independently.

Copilot uses AI. Check for mistakes.
Comment on lines 427 to 435
Copy link

Copilot AI Apr 13, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AView is used as a parameter type here, but the diff shows only using Android.Views; and no using AView = Android.Views.View; alias in the updated imports. If no alias exists earlier in the file, this will not compile. Fix by adding using AView = Android.Views.View; (preferred in MAUI Android platform code to avoid View name collisions) or by using the fully-qualified Android.Views.View type.

Copilot uses AI. Check for mistakes.
Comment on lines 185 to 201
Copy link

Copilot AI Apr 13, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new diagnostic MAUIX2015 (XCodeNotChildOfRoot) is declared, but ComputeXCodeSource currently only scans immediate root children and silently ignores nested x:Code. That can lead to confusing downstream XAML/SourceGen failures (nested x:Code remains in the node tree and/or is not emitted) without reporting the intended diagnostic. Consider scanning descendants for any x:Code nodes and explicitly reporting MAUIX2015 when an x:Code element is found outside the root’s immediate children (or remove the descriptor if nested x:Code is meant to be ignored).

Suggested change
static bool IsXCodeNode(XmlNode node) =>
node.LocalName == "Code" &&
(node.NamespaceURI == XamlParser.X2006Uri || node.NamespaceURI == XamlParser.X2009Uri);
void ReportNestedXCodeDiagnostics(XmlNode node)
{
foreach (XmlNode descendant in node.ChildNodes)
{
cancellationToken.ThrowIfCancellationRequested();
if (IsXCodeNode(descendant) && projItem.RelativePath is string nestedPath)
{
var lineInfo = descendant as IXmlLineInfo ?? new XmlLineInfo();
var location = LocationHelpers.LocationCreate(nestedPath, lineInfo, descendant.OuterXml);
diagnostics.Add(Diagnostic.Create(Descriptors.XCodeNotChildOfRoot, location));
}
ReportNestedXCodeDiagnostics(descendant);
}
}
// Find all x:Code child elements of the root and report any nested x:Code usage
var codeBlocks = new List<string>();
foreach (XmlNode child in root.ChildNodes)
{
cancellationToken.ThrowIfCancellationRequested();
if (IsXCodeNode(child))
codeBlocks.Add(child.InnerText);
ReportNestedXCodeDiagnostics(child);
}
if (codeBlocks.Count == 0)
return diagnostics.Count == 0 ? null : (projItem, string.Empty, diagnostics);

Copilot uses AI. Check for mistakes.
Comment on lines 436 to 458
Copy link

Copilot AI Apr 13, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test relies on several fixed Task.Delay(100) calls plus polling loops, which can add flakiness (timing-dependent) and slow the test suite. Prefer using existing device-test helpers like AssertEventually/WaitFor patterns (e.g., asserting that the internal scroll view exists and indicators update within a timeout) rather than hard-coded sleeps. This makes the test more robust across CI device performance variance.

Copilot uses AI. Check for mistakes.
Comment on lines 7 to 42
Copy link

Copilot AI Apr 13, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These new public interfaces expose types from Microsoft.Maui.Controls.Internals (ActionSheetArguments, AlertArguments, PromptArguments). Even with documentation, this effectively turns internal types into part of the public contract and can constrain future refactoring. Consider moving these argument types into a non-Internals namespace (or introducing public DTOs) and keeping the internals-only types as implementation details.

Suggested change
/// Provides platform-specific implementations for alert, action sheet, and prompt dialogs
/// used by the default <c>AlertManager</c> implementation.
/// <para>
/// This subscription contract is an implementation detail for framework/platform integration
/// and is not intended to be part of the public API surface.
/// </para>
/// <para>
/// Note: This interface is used by the default <c>AlertManager</c> implementation. If a custom
/// <see cref="IAlertManager"/> is registered via dependency injection, this interface will not
/// be resolved by the framework — the custom <see cref="IAlertManager"/> is responsible for
/// its own subscription management.
/// </para>
/// </summary>
// REVIEW: Changing this interface from public to internal is potentially a breaking change
// for external consumers and should not ship in a minor/service release without approval.
internal interface IAlertManagerSubscription
{
/// <summary>
/// Called when an action sheet is requested.
/// </summary>
void OnActionSheetRequested(Page sender, ActionSheetArguments arguments);
/// <summary>
/// Called when an alert dialog is requested.
/// </summary>
void OnAlertRequested(Page sender, AlertArguments arguments);
/// <summary>
/// Called when a prompt dialog is requested.

Copilot uses AI. Check for mistakes.
Comment on lines 32 to 34
Copy link

Copilot AI Apr 13, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change removes the previous guard that blocked reordering onto structural elements by checking target.ItemViewType. With the new logic, OnMove only checks the source view type, which (per the removed comment) is already blocked elsewhere by GetMovementFlags. This likely reintroduces the ability to drag regular items onto headers/footers/group headers/footers. Restore the target.ItemViewType structural-element check (and keep the source check if it’s still needed) to preserve the original behavior.

Suggested change
var targetItemViewType = target.ItemViewType;
if (sourceItemViewType == ItemViewType.Header || sourceItemViewType == ItemViewType.Footer
|| sourceItemViewType == ItemViewType.GroupHeader || sourceItemViewType == ItemViewType.GroupFooter
|| targetItemViewType == ItemViewType.Header || targetItemViewType == ItemViewType.Footer
|| targetItemViewType == ItemViewType.GroupHeader || targetItemViewType == ItemViewType.GroupFooter)

Copilot uses AI. Check for mistakes.
After PR dotnet#28713 added InvalidateMeasure to ShellFlyoutHeaderContainer,
the header height after scrolling may include the safe area margin
(~44px) depending on the content type. This is correct behavior -
InvalidateMeasure properly re-measures the header container including
its safe area margin.

Update the test assertions to:
- Accept header height between headerRequestedHeight and
  headerRequestedHeight + safeAreaTop (accommodates both ShellItems
  which don't trigger OnScrolled, and CollectionView/ScrollView which do)
- Use scrolledBox.Height instead of headerRequestedHeight in the scroll
  position assertion, making it self-consistent with the actual height
- Fix typos in assertion messages

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@kubaflo kubaflo force-pushed the fix/flyout-header-scroll-test branch from 096f301 to b3cd83d Compare April 13, 2026 21:52
@kubaflo
Copy link
Copy Markdown
Contributor Author

kubaflo commented Apr 13, 2026

/azp run maui-pr-uitests , maui-pr-devicetests

@azure-pipelines
Copy link
Copy Markdown

Azure Pipelines successfully started running 2 pipeline(s).

@github-actions
Copy link
Copy Markdown
Contributor

🧪 PR Test Evaluation

Overall Verdict: ✅ Tests are adequate

This is a test-only PR with one well-targeted change — updating device test assertions in FlyoutHeaderScroll to correctly accommodate behavior introduced by PR #28713. The fix is both necessary and correct.

👍 / 👎 — Was this evaluation helpful? React to let us know!

📊 Expand Full Evaluation

PR Test Evaluation Report

PR: #34932 — Fix FlyoutHeaderScroll device test regression from PR #28713
Test files evaluated: 1 (ShellFlyoutTests.cs)
Fix files: 0 (test-only PR)


Overall Verdict

✅ Tests are adequate

This is a test-only change that correctly updates FlyoutHeaderScroll device test assertions to accommodate the behavior change introduced by PR #28713 (InvalidateMeasure in ShellFlyoutHeaderContainer). The new assertions are more correct and actually reduce flakiness.


1. Fix Coverage — ✅

The test directly exercises the code path affected by PR #28713:

  • Opens the flyout, scrolls to the bottom, and checks header state
  • Validates scrolledBox.Height is within the correct range post-scroll
  • Validates that FlyoutHeaderBehavior.Scroll scrolls the header by its full actual height (self-consistent assertion)

2. Edge Cases & Gaps — ✅

Covered:

  • All 3 content types: ShellItems, CollectionView, ScrollView (12 total test cases: 4 behaviors × 3 content types)
  • All 4 FlyoutHeaderBehavior values: Default, Fixed, Scroll, CollapseOnScroll
  • iOS 26+ explicitly skipped with issue reference ([testing] Add iOS26 tests 26 - On Helix #33004)
  • Both height and scroll position assertions updated

Minor note:
The upper bound headerRequestedHeight + safeAreaTop covers both behavioral cases per the commit message: ShellItems don't trigger OnScrolled (height stays at headerRequestedHeight), while CollectionView/ScrollView do (height may include safeAreaTop).

3. Test Type Appropriateness — ✅

Current: Device Test (xUnit [Theory] + [ClassData])
Recommendation: Same — requires native iOS context (UIKit safe area APIs, native Shell rendering).

4. Convention Compliance — ✅

  • Uses [Theory] with [ClassData] for parameterization ✅
  • Async pattern with await and RunShellTest helper ✅
  • #if IOS guard correctly limits test to iOS ✅
  • Explicit tolerance values documented in assertions ✅

5. Flakiness Risk — ✅ Low

The changes reduce flakiness:

Assertion Old (too strict) New (correct)
Header height post-scroll CloseEnough(250, height, 0.3) — fails when safe area included height ∈ [249.7, 250 + safeAreaTop + 0.3] — accepts both cases
Scroll position (Scroll behavior) Y + 250 ≤ 0.3 — could give false pass Y + actualHeight ≤ 0.3 — self-consistent

iOS 26+ guard prevents known OS-level breaking changes from causing failures.

6. Duplicate Coverage — ✅ No duplicates

FlyoutHeaderScroll is the only test for this specific flyout scrolling behavior in the codebase.

7. Platform Scope — ✅

Correctly iOS-only (#if IOS). The affected ShellFlyoutHeaderContainer is in Compatibility/Handlers/Shell/iOS/ and requires UIKit APIs for safe area.

8. Assertion Quality — ✅

Assertion Quality
Height range: height ∈ [headerRequestedHeight - 0.3, headerRequestedHeight + safeAreaTop + 0.3] ✅ Bound to measured safeAreaTop, not a magic number
Scroll position: scrolledBox.Y + scrolledBox.Height ≤ 0.3 ✅ Self-consistent — uses actual height

Also fixes typos: scrolledBoy.YscrolledBox.Y, is no enoughis not enough.

9. Fix-Test Alignment — ✅

Test-only PR. Changes align precisely with behavior documented in PR #28713. The upper bound safeAreaTop is a measured value, not arbitrary.


Recommendations

  1. No action required — the changes are correct and well-targeted.
  2. (Optional) Consider adding a brief inline comment explaining why ShellItems keeps height ≈ headerRequestedHeight while CollectionView/ScrollView may add safeAreaTop — to help future maintainers understand the expected range.

🧪 Test evaluation by Evaluate PR Tests

@kubaflo kubaflo changed the base branch from inflight/current to inflight/candidate April 14, 2026 09:56
@kubaflo kubaflo merged commit fa5be52 into dotnet:inflight/candidate Apr 14, 2026
49 of 125 checks passed
kubaflo added a commit that referenced this pull request Apr 14, 2026
kubaflo added a commit that referenced this pull request Apr 14, 2026
### Description of Change

Fixes the `FlyoutHeaderScroll` device test regression introduced by PR
#28713.

After PR #28713 added `IPlatformMeasureInvalidationController` to
`ShellFlyoutHeaderContainer`, the header height after scrolling may
include the safe area margin (~44px) depending on the content type.

**Changes (test only, no core code changes):**
- Height assertion: accept header height between `headerRequestedHeight`
and `headerRequestedHeight + safeAreaTop`
- Scroll position assertion: use `scrolledBox.Height` instead of
hardcoded `headerRequestedHeight`
- Fix typos in assertion messages

### Issues Fixed

Fixes FlyoutHeaderScroll device test regression from #28713

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
PureWeen added a commit that referenced this pull request Apr 15, 2026
…, security hardening (#34678)

## Description

Overhauls the `copilot-evaluate-tests` gh-aw workflow — switches to
on-demand triggers only (`/evaluate-tests` slash command + manual
`workflow_dispatch`), adds security hardening, and improves error
handling. No auto-runs on PR create/update.

### What Changed

**Triggers (on-demand only — no auto-runs)**
- Add `slash_command: evaluate-tests` — comment `/evaluate-tests` on a
PR to trigger
- Keep `workflow_dispatch` — manual trigger from Actions tab with PR
number input
- Disable `pull_request_target` — no auto-evaluation on PR create/update
- Add `bots: ["copilot-swe-agent[bot]"]` — Copilot-authored PRs can be
evaluated
- Add `labels: ["pr-review", "testing"]` — workflow runs are labeled

**Gate step (fast-fail for invalid requests)**
- Check PR is OPEN before evaluating (rejects closed/merged PRs with
clear message)
- Check for test source files in diff before spinning up agent
- Fall back to REST API for PRs with 300+ files (where `gh pr diff`
returns HTTP 406)
- All API errors surfaced with clear messages — no silent masking
- `exit 1` stops the workflow immediately — no wasted agent compute

**Access gating (`Checkout-GhAwPr.ps1`)**
- Reject fork PRs (`isCrossRepository` check)
- Verify PR author has write access (admin/write/maintain roles)
- Fix `ConvertFrom-Json` ordering — check exit code before JSON parsing
- Make infrastructure restore fatal on failure (was soft warning)
- Remove pre-delete pattern — `git checkout` overwrites in-place

**Workflow improvements**
- `hide-older-comments: true` — previous evaluations auto-collapse
- `report-as-issue: false` for noop — no issue created when nothing to
evaluate
- Timeout bumped 15 → 20 minutes
- Dry-run mode via `suppress_output` input (workflow_dispatch only)
- `Gather-TestContext.ps1` now receives `-PrNumber` parameter

**Security documentation (`gh-aw-workflows.instructions.md`)**
- Add "Before You Build" anti-patterns table — prefer built-in gh-aw
features
- Add Security Boundaries section with defense layers table
- Add Rules for gh-aw Workflow Authors (DO/DON'T list)
- Document `COPILOT_TOKEN` exposure and mitigations
- Add `slash_command` to fork behavior table
- Update `Checkout-GhAwPr.ps1` description to match current behavior

### Trigger Behavior

| Trigger | When it fires | Who can trigger |
|---------|---------------|-----------------|
| `/evaluate-tests` comment | Comment on a PR | Write-access
collaborators + copilot-swe-agent[bot] |
| `workflow_dispatch` | Actions tab → "Run workflow" → enter PR number |
Write-access collaborators |
| ~~`pull_request_target`~~ | ~~Auto on PR create/update~~ |
~~Disabled~~ |

### Security Model

Based on [GitHub Security Lab
guidance](https://securitylab.github.com/resources/github-actions-preventing-pwn-requests/):

- PR contents treated as **passive data** (read/analyze, never built or
executed)
- Agent runs in **sandboxed container** with `GITHUB_TOKEN` and `gh` CLI
scrubbed
- Write operations in **separate `safe_outputs` job** (not the agent)
- Agent output limited to `max: 1` comment via safe-outputs
- `Checkout-GhAwPr.ps1` rejects fork PRs and verifies write access
before checkout
- Infrastructure restore is fatal on failure — prevents running with
untrusted infra

### Validation

| Test | PR | Result |
|------|-----|--------|
| Open PR with tests | #34983 | ✅ Full success (gate → checkout → agent
→ comment) |
| No-test PR | #34876 | ✅ Gate fast-fail ("no test source files") |
| Merged PR | #34932 | ✅ Gate fast-fail ("MERGED — skipping") |

### Known Limitations

- Fork PRs via `/evaluate-tests` can supply modified `.github/skills/` —
accepted residual risk (agent sandboxed, output bounded). Tracked as
[gh-aw#18481](github/gh-aw#18481)
- `exit 1` in gate step shows ❌ in GitHub checks for no-test/closed PRs
— intentional (no built-in "skip" mechanism in gh-aw steps)
- `pull_request_target` commented out — can be re-enabled later for
auto-evaluation

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Ahamed-Ali pushed a commit that referenced this pull request Apr 22, 2026
<!-- 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 the `FlyoutHeaderScroll` device test regression introduced by PR

After PR #28713 added `IPlatformMeasureInvalidationController` to
`ShellFlyoutHeaderContainer`, the header height after scrolling may
include the safe area margin (~44px) depending on the content type. This
is correct behavior — `InvalidateMeasure` properly re-measures the
header container including its safe area margin.

**Changes (test only, no core code changes):**
- Height assertion: accept header height between `headerRequestedHeight`
and `headerRequestedHeight + safeAreaTop` (accommodates both ShellItems
which don't trigger `OnScrolled`, and CollectionView/ScrollView which
do)
- Scroll position assertion: use `scrolledBox.Height` instead of
hardcoded `headerRequestedHeight`, making it self-consistent with the
actual measured height
- Fix typos in assertion messages

Fixes FlyoutHeaderScroll device test regression from #28713

---------
Ahamed-Ali pushed a commit that referenced this pull request Apr 22, 2026
### Description of Change

Fixes the `FlyoutHeaderScroll` device test regression introduced by PR
#28713.

After PR #28713 added `IPlatformMeasureInvalidationController` to
`ShellFlyoutHeaderContainer`, the header height after scrolling may
include the safe area margin (~44px) depending on the content type.

**Changes (test only, no core code changes):**
- Height assertion: accept header height between `headerRequestedHeight`
and `headerRequestedHeight + safeAreaTop`
- Scroll position assertion: use `scrolledBox.Height` instead of
hardcoded `headerRequestedHeight`
- Fix typos in assertion messages

### Issues Fixed

Fixes FlyoutHeaderScroll device test regression from #28713

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
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.