Skip to content

Conversation

@PureWeen
Copy link
Member

Note

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

Merges main branch changes into net10.0.

rmarinho and others added 30 commits December 18, 2025 13:31
### Description of Change

This pull request adds new pipeline stages to enable running device
tests using the CoreCLR runtime for iOS, MacCatalyst, and Android
platforms in the Azure DevOps pipeline. The changes introduce a
dedicated build stage for CoreCLR device tests and new test execution
stages for each platform, improving test coverage and aligning with
CoreCLR support.

New CoreCLR device test pipeline stages:

* Build Stage:
- Added a new `devicetests_build_coreclr` stage to build device tests
with CoreCLR, including steps for restoring, building, and publishing
artifacts for downstream test stages.

* Test Execution Stages:
- Introduced `devicetests_ios_coreclr`, `devicetests_catalyst_coreclr`,
and `devicetests_android_coreclr` stages to run device tests on iOS,
MacCatalyst, and Android using CoreCLR. Each stage downloads the CoreCLR
build artifacts and submits tests to Helix.

* Artifact Management:
- Updated artifact publishing steps to handle both successful and failed
builds for CoreCLR device tests, ensuring test execution stages have the
necessary binaries.
`HybridWebView.js.map` is a TypeScript compilation artifact that should
not be tracked in source control. Without an explicit gitignore pattern,
Copilot PRs and other automated tools have been attempting to commit
this file.

## Changes

- Added `*.js.map` pattern to `.gitignore` to exclude JavaScript source
maps
- Removed previously committed `HybridWebView.js.map` from git tracking

## Pattern Specificity

The `*.js.map` pattern targets only JavaScript source maps. Existing
`.css.map` files (Bootstrap dependencies in templates) remain tracked as
intended since they have different file extensions.

```
# Before: HybridWebView.js.map appeared as untracked
$ git status
Untracked files:
  src/Core/src/Handlers/HybridWebView/HybridWebView.js.map

# After: File is properly ignored
$ git check-ignore -v HybridWebView.js.map
.gitignore:389:*.js.map
```

<!-- START COPILOT CODING AGENT SUFFIX -->



<!-- START COPILOT ORIGINAL PROMPT -->



<details>

<summary>Original prompt</summary>

> all copilot PRs keep checking in
src/Core/src/Handlers/HybridWebView/HybridWebView.js.map can you figure
out why that has started happening and how we should fix?


</details>



<!-- START COPILOT CODING AGENT TIPS -->
---

✨ Let Copilot coding agent [set things up for
you](https://github.com/dotnet/maui/issues/new?title=✨+Set+up+Copilot+instructions&body=Configure%20instructions%20for%20this%20repository%20as%20documented%20in%20%5BBest%20practices%20for%20Copilot%20coding%20agent%20in%20your%20repository%5D%28https://gh.io/copilot-coding-agent-tips%29%2E%0A%0A%3COnboard%20this%20repo%3E&assignees=copilot)
— coding agent works faster and does higher quality work when set up for
your repo.

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: PureWeen <5375137+PureWeen@users.noreply.github.com>
This pull request introduces improvements to the CarouselView feature
and enhances the reliability of drag-and-drop UI tests. The most
significant changes include updating the way the "Apply" action is
triggered in the CarouselView options page, making CarouselView scroll
operations non-animated, and improving the robustness of UI tests for
both CarouselView and drag-and-drop scenarios.

**CarouselView improvements:**
* Moved the "Apply" action from a toolbar item to a button within the
main content area in `CarouselViewOptionsPage.xaml` for better
accessibility and user experience.
* Changed the `ScrollTo` method in `CarouselViewControlPage.xaml.cs` to
disable animation, ensuring immediate navigation to the selected item.

**UI test reliability enhancements:**
* Improved the drag-and-drop UI test in `DragAndDropUITests.cs` by
retrying the drag-and-drop operation up to three times and asserting
failure if the drop event does not fire, increasing test robustness
against flakiness.
* Added a retry mechanism for the `VerifyCarouselViewWithScrollTo` test
on iOS, re-attempting the scroll action if the expected item does not
appear, to handle platform-specific flakiness.
### Description of Change

Use netcore public when possible
### Description of Change

Specify the pool to send the tests
…d of UI navigation (#31673)

### Description of Change

#### This PR reopens the work from the closed
[#30651](#30651)

This pull request introduces functionality to streamline test execution
by allowing tests to be launched directly via startup arguments or
programmatically. Key changes include adding support for passing test
names as environment variables, introducing a `PageFactory` mechanism
for dynamic page creation, and enhancing test lifecycle management for
specific platforms like Mac Catalyst.

Note: This PR is an extension of the
PR(#30286) with a proper
implementation.

#### Test Execution Enhancements:

- **Startup Argument Support**: Added logic to retrieve test names from
environment variables and dynamically load the corresponding test page
in `CreateDefaultMainPage()` (`MauiProgram.cs`).

- **Dynamic Page Creation**: Introduced a `PageFactory` property in the
`IssueModel` class to enable dynamic test page instantiation
(`TestCases.cs`).

- **Direct Test Page Retrieval**: Implemented `TryToGetTestPage()` in
`TestCaseScreen` to retrieve test pages by description using
`PageFactory` (`TestCases.cs`).

#### Platform-Specific Test Lifecycle Improvements:

- **Mac Catalyst Test Configuration**: Enhanced `_IssuesUITest` to pass
test names as startup arguments and manage app launch/close lifecycle
specifically for Mac Catalyst (`_IssuesUITest.cs`).
[[1]](https://github.com/dotnet/maui/pull/30651/files#diff-6e8c0ea1f2979b5484d14facb1aa5b2ccef3ee748f1606eaf46220ac0936e3edR22-R46)
[[2]](https://github.com/dotnet/maui/pull/30651/files#diff-6e8c0ea1f2979b5484d14facb1aa5b2ccef3ee748f1606eaf46220ac0936e3edR58-R61)

- ** Appium Options for Mac Catalyst**: Updated `AppiumCatalystApp` to
include environment variables in Appium options for test execution
(`AppiumCatalystApp.cs`).

#### Helper Methods for Test Execution:

- **Parameterized App Launch**: Added a method to launch apps with
additional parameters, supporting test-specific configurations
(`HelperExtensions.cs`).

- **Mac-Specific App Closure**: Modified `FixtureOneTimeTearDown()` to
handle app closure for Mac Catalyst during test teardown
(`UITestBase.cs`).

 ### Output


https://github.com/user-attachments/assets/82fc12ad-b0d3-4bad-901f-0ab6b2bddda1



### Issues Fixed

Fixes #30285
- [x] Apply changes from PR #33101 to AppThemeBinding.cs (make class
public for NET11+, make AppThemeResource public)
- [x] Apply changes from PR #33101 to KnownMarkups.cs (replace helper
methods with object initializers)
- [x] Apply changes from PR #33101 to XamlGenerator.cs (remove
AppThemeBindingHelpers generation)
- [x] Apply changes from PR #33101 to test file (wrap with
NET11_0_OR_GREATER)
- [x] Apply changes from PR #33107 to KnownMarkups.cs (wrap method with
NET11_0_OR_GREATER)
- [x] Apply changes from PR #33107 to NodeSGExtensions.cs (wrap
dictionary entry with NET11_0_OR_GREATER)
- [x] Verify changes compile successfully
- [x] Run relevant tests to ensure functionality (123 SourceGen tests
passed, 34 AppTheme XAML tests passed)
- [x] Code review completed (no issues found)
- [x] Security scan completed (no vulnerabilities found)
- [x] Remove unrelated HybridWebView.js.map file (per review feedback)

<!-- START COPILOT CODING AGENT SUFFIX -->



<!-- START COPILOT ORIGINAL PROMPT -->



<details>

<summary>Original prompt</summary>

> Please apply the changes from
#33101 and
#33107 to the main branch


</details>



<!-- START COPILOT CODING AGENT TIPS -->
---

✨ Let Copilot coding agent [set things up for
you](https://github.com/dotnet/maui/issues/new?title=✨+Set+up+Copilot+instructions&body=Configure%20instructions%20for%20this%20repository%20as%20documented%20in%20%5BBest%20practices%20for%20Copilot%20coding%20agent%20in%20your%20repository%5D%28https://gh.io/copilot-coding-agent-tips%29%2E%0A%0A%3COnboard%20this%20repo%3E&assignees=copilot)
— coding agent works faster and does higher quality work when set up for
your repo.

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: PureWeen <5375137+PureWeen@users.noreply.github.com>
…c M3 styles (#33074)

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

### Description of Change

<!-- Enter description of the fix in this section -->
This pull request introduces the foundation for Material Design 3 (M3)
support in .NET MAUI for Android. The changes allow developers to opt
into Material Design 3 via a new MSBuild property, provide runtime
detection of the Material version, and add the necessary Android
resources and styles for M3. By default, apps continue to use Material
Design 2 to maintain backward compatibility.

**Material Design 3 opt-in and configuration:**

- Added a new MSBuild property `UseMaterial3` (default: false) to allow
developers to opt into Material Design 3 for Android. Internal build
properties are set based on this value, and a build log message informs
developers which Material version is active.
(`Microsoft.Maui.Controls.Common.targets`)
[[1]](diffhunk://#diff-6d91a62f494b70d0fb3dcd4fd0020ef5f83dcbbc9c8240b5cad98fc1058c6061R14-R36)
[[2]](diffhunk://#diff-6d91a62f494b70d0fb3dcd4fd0020ef5f83dcbbc9c8240b5cad98fc1058c6061R57-R76)
- The Material Design version is now surfaced as a runtime feature
switch (`Microsoft.Maui.RuntimeFeature.IsMaterial3Enabled`), which is
set during the build process if `UseMaterial3` is specified.
(`Microsoft.Maui.Controls.targets`, `RuntimeFeature.cs`)
[[1]](diffhunk://#diff-59cefde4ef74a9adcaff4aa8bc3271bd2d795d2b4214d7d56327b5edcca73c14R354-R357)
[[2]](diffhunk://#diff-0fd447e39a83800f1f629c61f23d5a18a5c4d3d30fa3ecd81dbafbc15eb3a402R30)
[[3]](diffhunk://#diff-0fd447e39a83800f1f629c61f23d5a18a5c4d3d30fa3ecd81dbafbc15eb3a402R151-R158)

**Runtime and theming support:**

- Introduced `MaterialDesignHelper` for runtime detection of Material
Design 3 enablement, with caching for performance and a method to
retrieve the current Material version. (`MaterialDesignHelper.cs`)
- Updated `MauiMaterialContextThemeWrapper` to dynamically select the
correct base theme (Material 2 or Material 3) at runtime using the new
helper. (`MauiMaterialContextThemeWrapper.cs`)

**Android resource additions for Material Design 3:**

- Added comprehensive Material Design 3 color definitions for both light
and dark themes, including compatibility colors for legacy components.
(`colors-material3.xml`)
- Added Material Design 3 style definitions, including base theme, no
action bar variant, action mode, and splash theme.
(`styles-material3.xml`)

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

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

### Output
| Material 2 | Material 3|
|--|--|
| <img width="1080" height="1920" alt="M2"
src="https://github.com/user-attachments/assets/877c4133-6f08-40da-90c3-d685d9e1b882"
/> | <img width="1080" height="1920" alt="M3"
src="https://github.com/user-attachments/assets/e2cebb4b-8438-4858-b453-414c2e03b504"
/> |
<!-- 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

Split iOS device tests by category in Helix to work around an unknown
issue that causes tests to take over an hour when run together, vs under
15 minutes when split.

## Problem

When running all Controls.DeviceTests categories together on iOS in CI,
the test run takes over an hour. When the same tests are split by
category, they complete in under 15 minutes total. This issue only
reproduces in CI - we haven't been able to reproduce it locally. Ideally
we'd find and fix the root cause, but until then this workaround keeps
CI times reasonable.

## Changes

### Helix Configuration (`eng/helix_xharness.proj`)
- Add `ControlsTestCategoriesToSkipForRestOfTests` property defining
heavy categories to run individually
- Split Controls.DeviceTests into 4 work items for iOS:
- 3 heavy categories run separately: `CollectionView`, `Shell`,
`HybridWebView`
  - 1 "Other" work item runs all remaining categories
- Core.DeviceTests runs as a single work item (no splitting)
- MacCatalyst and Android unchanged

### Test Infrastructure
(`src/Core/tests/DeviceTests.Shared/DeviceTestSharedHelpers.cs`)
- Add support for `TestFilter=SkipCategories=X,Y,Z` environment variable
- Allows skipping multiple categories via comma or semicolon-separated
list
- Existing `TestFilter=Category=X` support unchanged

### Documentation
(`.github/instructions/helix-device-tests.instructions.md`)
- Add Copilot instructions for Helix device test configuration
- Document category splitting, local execution, and troubleshooting

## Result

iOS device tests now run as ~8 parallel work items instead of 5, with
the slowest categories isolated. This brings iOS device test time from
1+ hour down to ~15 minutes.
This pull request makes minor adjustments to the shadow feature test UI
and test logic. The main change is the removal of the numeric keyboard
from several entry fields in the shadow feature test page, and a small
improvement to the shadow color test to clear the color entry before
entering a new value.

UI adjustments in shadow feature page:

* Removed the `Keyboard="Numeric"` property from the `OffsetXEntry`,
`OffsetYEntry`, `RadiusEntry`, and `OpacityEntry` input fields in
`ShadowFeaturePage.xaml`, so these fields will now use the default
keyboard instead of the numeric keyboard.

Test improvements:

* Updated the `Shadow_SetColor` test to clear the `ColorEntry` field
before entering a new color value, ensuring the test input is not
affected by any previous value.
…mple Projects (#33282)

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

### Description of Change

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

<!-- 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. -->
This pull request introduces a new `UseMaterial3` property, set to
`false` by default, across several project files. It also adds
documentation comments to clarify that if this flag is changed,
developers must delete the `artifacts` folder and rebuild the entire
codebase to avoid build issues.

Configuration and documentation updates:

* Added the `<UseMaterial3>false</UseMaterial3>` property to
`Maui.Controls.Sample.Sandbox.csproj`, `Maui.Controls.Sample.csproj`,
and `Controls.TestCases.HostApp.csproj`, along with comments explaining
the need to clean artifacts and rebuild if the flag is changed.
[[1]](diffhunk://#diff-d99ae49df2f89869b0081632bce2fb57dff57b036662b3f6c8e5feeb6a66ef9dR16-R18)
[[2]](diffhunk://#diff-af863121b6168780847c8835ede77fb1b01120fa3f9dd5fb48a846128f9c08fdR18-R20)
[[3]](diffhunk://#diff-c73eb67a2a515f32003a6bdc59df7156d492dfcafb1b828723476f67cd439103R16-R18)

Fixes #

<!--
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!
### Root Cause of the issue



- Upon analysis, in iOS versions 18.4 and 18.0, GetCell is called when
PopAsync is invoked—navigating back to the MainPage (which contains the
CarouselView). At this point, _carouselViewLoopManager is properly set
through the Setup method, and a valid IndexPath is passed into GetCell,
resulting in correct updates.

- However, in iOS versions 15.5 and 16.0, GetCell is triggered
immediately when _viewModel.Source[0] is updated. At that time, the
CarouselViewLoopManager is still null, and since a valid index cannot be
retrieved, the else part of GetCell is executed with an invalid
IndexPath, leading to an ArgumentNullException.



### Description of Change



- To prevent ArgumentNullException caused by invalid IndexPath values
during the loop initialization phase, ensure that the
CarouselViewLoopManager is fully initialized before it is used. Once
initialized, it correctly returns a valid IndexPath using
FromRowSection, allowing the templated cell to be updated as expected in
all iOS versions.



### Issues Fixed



Fixes #28557 



### Tested the behaviour in the following platforms



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

### Tested Version

| iOS Version | App Crashed |
|----------|----------|
| 15.5 | Yes |
| 16.0 | Yes |
| 17.4 | No |
| 18.4 | No |


### Screenshot

### iOS 15.5

| Before Issue Fix | After Issue Fix |
|----------|----------|
| <video
src="https://github.com/user-attachments/assets/4df6973e-c658-4c89-a085-cc2035a593bf">
| <video
src="https://github.com/user-attachments/assets/25840d66-fa41-4a00-8e53-a262b405eca3">
|

### iOS 16.0

| Before Issue Fix | After Issue Fix |
|----------|----------|
| <video
src="https://github.com/user-attachments/assets/882829ed-fef4-40e9-a450-2972b4134c8e">
| <video
src="https://github.com/user-attachments/assets/4a5d0c48-e340-4b51-939e-6a6bf7518db7">
|

### iOS 17.4 && iOS 18.4


| iOS 17.4 | iOS 18.4 |
|----------|----------|
| <video
src="https://github.com/user-attachments/assets/5c167003-f5af-4989-87c7-d09b8d3b7b81">
| <video
src="https://github.com/user-attachments/assets/9c44bc1d-0956-4795-994b-2c4c314d40a7">
|
…yQueryAttributes(query) (#25663)

* Fix - 13573 fix changes

* Testcases commit

* Update Shell.cs

* commit for testcases

* commit for review changes

* Commit for class name changes

* Commit class name change

* Commit for review

* commit for testcase changes

* Commit for images

* Test case changes

* Update Issue13537.cs

* Update Issue13537.cs

* Update Issue13537.cs

* Update Shell.cs

* Update Shell.cs

* changes for initial triggering

* comments updated

* Update Issue13537.cs

* Update Issue13537.cs

commit testcase failure

* Changes for indentation

* Review changes

* Update Issue13537.cs
…tes (#33006)

Added unit tests to validate the fix for issues #13537 and #28453:

- TabBarNavigationSetsQueryAttributesProperty: Verifies QueryAttributesProperty is set when switching tabs
- TabBarNavigationWithGoToAsyncSetsQueryAttributesProperty: Verifies GoToAsync navigation sets property
- FlyoutItemNavigationSetsQueryAttributesProperty: Verifies FlyoutItem navigation triggers ApplyQueryAttributes
- NavigatingBackToTabSetsQueryAttributesProperty: Verifies navigating back to a tab sets property
- PopNavigationTriggersApplyQueryAttributes: Verifies pop navigation restores parameters
- ShellSectionChangedSetsQueryAttributesProperty: Verifies ShellSection changes trigger ApplyQueryAttributes

These tests ensure IQueryAttributable.ApplyQueryAttributes is called for all Shell navigation types.
…T>.Count (#32912)

* Fix binding to interface-inherited properties like IReadOnlyList<T>.Count

Fixes #13872

The runtime binding expression was not resolving properties inherited from parent interfaces. For example, IReadOnlyList<T>.Count would return null because Count is defined on IReadOnlyCollection<T>, not directly on IReadOnlyList<T>.

Added GetProperty() method that searches both base classes and implemented interfaces recursively, similar to how GetIndexer() already handles this case.

* Fix Maui32879Tests expected output after pragma warning merge
…32961)

When a Button inside a ControlTemplate has both Command and CommandParameter
with TemplateBinding, the async binding application path can cause Command
to be evaluated before CommandParameter resolves, resulting in CanExecute
being called with null parameter.

## Solution
Add a BindableProperty dependency mechanism via DependsOn() method. When
CommandProperty.DependsOn(CommandParameterProperty) is registered, the
CommandElement.GetCanExecute() forces the CommandParameter binding to apply
before calling CanExecute. This ensures the parameter value is available.

## Changes
- BindableProperty: Add Dependencies property and DependsOn() method
- BindableObject: Add ForceBindingApply() to force a binding to apply immediately
- CommandElement: Force dependency bindings to apply before calling CanExecute
- ButtonElement, CheckBox, SearchBar, MenuItem, RefreshView, TextCell: Register
  Command -> CommandParameter dependency
- All ICommandElement implementations: Pass CommandProperty to GetCanExecute()

## Testing
Added unit tests that verify:
1. Initial template binding works correctly
2. CommandParameter is preserved after reparenting (the bug scenario)
…ings (#32954)

* Initial plan

* Add RelayCommand support to binding source generators

Co-authored-by: simonrozsival <374616+simonrozsival@users.noreply.github.com>

* Consolidate RelayCommand detection logic into shared BindingSourceGen code

Co-authored-by: simonrozsival <374616+simonrozsival@users.noreply.github.com>

* Remove test files that cannot work in test environment

Co-authored-by: simonrozsival <374616+simonrozsival@users.noreply.github.com>

* Refactor RelayCommand logic into shared helper method

Co-authored-by: simonrozsival <374616+simonrozsival@users.noreply.github.com>

* Move RelayCommand logic to PathParser with special case handling

Co-authored-by: simonrozsival <374616+simonrozsival@users.noreply.github.com>

* Simplify TryGetProperty naming and add RelayCommand unit tests

Co-authored-by: simonrozsival <374616+simonrozsival@users.noreply.github.com>

* Enable expression-based bindings with RelayCommand support

Co-authored-by: simonrozsival <374616+simonrozsival@users.noreply.github.com>

* Apply suggestion from @Copilot

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

---------

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: Šimon Rozsíval <simon@rozsival.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Changed the condition for setting PrefersLargeTitles to enable large titles unless the display mode is set to Never, improving compatibility with different display modes.
)

### Issue Detail
When navigating using PushAsync and PopToRootAsync, the destructor was
not invoked when using PopToRootAsync.
 
### Root Cause
The TitleView was not properly disposed when using PopToRootAsync.
 
### Description of Change
Resolved the issue by disposing the TitleView using Disconnect, similar
to PopAsync.

Reference:
https://github.com/dotnet/maui/blob/main/src/Controls/src/Core/Compatibility/Handlers/NavigationPage/iOS/NavigationRenderer.cs#L345

### Issues Fixed

Fixes #28201

### Screenshots

| Before Issue Fix | After Issue Fix |
|----------|----------|
| <video width="300" height="600"
src="https://github.com/user-attachments/assets/23f571a4-f6f9-4978-b6d8-0a334b2dd593">
| <video width="300" height="600"
src="https://github.com/user-attachments/assets/0c7e5202-bf49-4fbd-98b1-3028c1cbc8c8">)
|
…ceholder text (#30407)

<!-- 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 CharacterSpacing property in SearchBar behaves inconsistently across
platforms:

1) iOS/macOS: Not applied to search text or placeholder—both use default
spacing.
2) Windows: Applied to search text, but placeholder still shows default
spacing.

### Root Cause

**iOS**: The CharacterSpacing logic for SearchBar is defined in
UpdateCharacterSpacing() in TextFieldExtensions.cs, shared across Entry,
Editor, and SearchBar.

For Entry and Editor, it's triggered via MapFormatting(), which is
called from MapText() (e.g., in Entry.iOS), ensuring spacing is applied
on load and text change.
For SearchBar, MapFormatting() was not invoked, so CharacterSpacing
was never applied to either the search text or placeholder.

**Windows** - The CharacterSpacing property of the SearchBar is applied
only to the search text via the AutoSuggestBox.CharacterSpacing
property. However, the placeholder text is rendered by a separate
internal TextBlock that does not inherit this spacing, resulting in
default character spacing for the placeholder.

### Description of Change
**iOS**: The fix involves explicitly calling MapFormatting() within the
MapText() method of SearchBar.iOS.

**Windows**: The fix explicitly locates the TextBlock rendering the
placeholder using GetFirstDescendant<TextBlock>() and applies the same
CharacterSpacing to it within the OnLoaded lifecycle of the
AutoSuggestBox. This ensures that both search text and placeholder text
reflect the correct spacing consistently.

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

### Issues Fixed:
Fixes #30366 

### Screenshots

| | Before  | After |
|----------|---------|--------|
|PlaceHolder Text|  <img
src="https://github.com/user-attachments/assets/04e443ac-3931-4d2d-a51f-9947557cbb45">
|  <img
src="https://github.com/user-attachments/assets/63ab8cca-7e3e-40d3-b82d-2cc8f27f4965"> 
|
|SearchBar Text|  <img
src="https://github.com/user-attachments/assets/157a4b5a-c0e0-4424-9a0f-f2c9fea2432b">
|  <img
src="https://github.com/user-attachments/assets/dcecca30-081f-4ba7-bfda-000d2618fda3"> 
|
…lyst (#26825)

### Issue Details
When scrolling a ScrollView using the ScrollToAsync method with the
ScrollPosition.Center value, the item is incorrectly centered.

### Root Cause
The scroll offset values allow items to scroll into a negative position,
causing the item to be incorrectly centered.
 
### Description of Change
We clamp the scroll offset values to ensure the item does not scroll
into a negative position.

### 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 #26760 
Fixes #28965 

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

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

### Output Screenshot
| Before  | After  |
|---------|--------|
| <video
src="https://github.com/user-attachments/assets/be05f838-42bf-452c-bc1a-a34d02de006f"
width="320" height="240" controls></video> | <video
src="https://github.com/user-attachments/assets/02ca23f1-0d31-49c5-9862-767235845761"
width="320" height="240" controls></video> |

---------

Co-authored-by: Shalini-Ashokan <102292178+Shalini-Ashokan@users.noreply.github.com>
…n Mac Platform (#26701)

### Issue Details

- FlyoutPage.Flyout disappeared When the FlyoutPage is maximized

### Root Cause

- When the FlyoutPage is maximized, the flyout is removed even when
IsPresented is true. This causes the FlyoutPage's flyout to disappear.

### Description of Change

- When maximizing the Mac window, the FlyoutPage is removed in the
ViewWillTransitionToSize(CoreGraphics.CGSize toSize,
IUIViewControllerTransitionCoordinator coordinator) method using the
UpdatePresented(bool value) method. Here, there is no need to update the
FlyoutPage when maximizing the Mac window.
- Therefore, I have ignored the FlyoutPage update in this scenario.

### Issues Fixed
Fixes #22719

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

### Test Case:
This issue occurs only when resizing the Mac window, and we could not
find a way to automate the dynamic resizing of Mac windows. Therefore,
test cases for this scenario are not included in this PR.

### Output

|Before|After|
|--|--|
| <video
src="https://github.com/user-attachments/assets/079fceb7-02cd-430c-be9c-014838a1dc4f"
>| <video
src="https://github.com/user-attachments/assets/e13d7b26-5090-409b-aaf6-e375791854c2">|

---------

Co-authored-by: Shane Neuville <shneuvil@microsoft.com>
Co-authored-by: Javier Suárez <javiersuarezruiz@hotmail.com>
…SizingStrategy (#33161)

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

### Root cause

- When the ItemSizingStrategy is set to measure the first item, the
collection view’s grouped item header or footer template size is taken
from the first item’s size instead of the actual size of the grouped
item header or footer template.

### Description of changes

- I have ignored the first measured item’s size when applying it to the
grouped header or footer template, the collection view header or footer
view, and the collection view header or footer template in
PreferredLayoutAttributesFittingAttributes of TemplatedCell2.

### Issues Fixed

Fixes #33130 

Validated the behaviour in the following platforms
- [x] Android
- [x] Windows ,
- [x]  iOS, 
- [x] MacOS

### Output
**iOS**

|Before|After|
|--|--|
| <video
src="https://github.com/user-attachments/assets/deb143a4-5df0-463d-915c-16254bad658c"
>| <video
src="https://github.com/user-attachments/assets/56ba82de-f6d9-4b05-8ea7-2a12031282eb">|

**macOS**
|Before|After|
|--|--|
| <video
src="https://github.com/user-attachments/assets/094b3e91-8198-483f-8b87-f3f5777e48b4"
>| <video
src="https://github.com/user-attachments/assets/5937e5c3-feb5-4138-8c47-9f7a31095a16">|
This pull request introduces a platform-specific adjustment to the
`VerifyVerticalTextAlignmentBasedOnCharacterSpacing` test to improve its
reliability on Windows. The main change ensures that the test interacts
with the UI in a way that accounts for the behavior of the Entry control
cursor on Windows.

Platform-specific test reliability:

* Updated the `VerifyVerticalTextAlignmentBasedOnCharacterSpacing` test
in `EntryFeatureTests.cs` to tap the `TextChangedLabel` on Windows after
applying changes, ensuring the Entry control cursor does not interfere
with screenshot verification.
@kubaflo kubaflo added area-controls-checkbox CheckBox area-controls-picker Picker area-layout StackLayout, GridLayout, ContentView, AbsoluteLayout, FlexLayout, ContentPresenter and removed area-layout StackLayout, GridLayout, ContentView, AbsoluteLayout, FlexLayout, ContentPresenter area-controls-checkbox CheckBox area-controls-picker Picker labels Dec 30, 2025
StephaneDelcroix and others added 6 commits December 30, 2025 11:48
<!-- 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

When a binding has a `Source` property with a `RelativeSourceExtension`,
skip the compiled binding path and use the fallback string-based binding
instead.

### Root Cause

When PR #32925 added RelativeSource support to SourceGen, it also
inadvertently caused bindings with `RelativeSource` to be compiled using
`x:DataType` as the source type. This is incorrect because the source
type for `RelativeSource` bindings is determined at runtime, not at
compile time.

For example, with:
```xml
<ContentView x:DataType="local:ChildViewModel">
    <TapGestureRecognizer CommandParameter="{Binding Path=., Source={RelativeSource AncestorType={x:Type local:MainPage}}}" />
</ContentView>
```

The SourceGen was generating:
```csharp
return new TypedBinding<ChildViewModel, ChildViewModel>(
    getter: source => (source, true),  // Returns ChildViewModel, not MainPage!
    ...
);
```

### Fix

Added a check in `ProvideValueForBindingExtension` to detect when the
binding has a `Source` property with a `RelativeSourceExtension`. In
this case, we skip the compiled binding path and use the fallback
string-based binding instead, which correctly resolves the source at
runtime.

### Issues Fixed
Fixes #33247

### Testing

- Added SourceGen unit tests to verify correct code generation
- Added XAML unit tests to verify the binding works correctly at runtime
## Description

This PR enforces that every UI test has **exactly one** `[Category]`
attribute through a build-time analyzer. Previously, tests could have
multiple categories which caused them to run multiple times in CI.

Each test needs to have a category so they are ran in the CI pipeline.
With this analyzer we ensure that this is not forgotten during
development.

## Changes

### Analyzer Updates (`NUnitTestMissingCategoryAnalyzer`)

- **MAUI0001**: Build error when a test has **no** category
- **MAUI0002**: Build error when a test has **more than one** category
- Changed severity from `Warning` to `Error`
- Excludes platform-specific ignore attributes
(`FailsOnAndroidWhenRunningOnXamarinUITest`, etc.) from the category
count since they conditionally derive from `CategoryAttribute`

### Fixed 314 Tests

All tests with multiple categories have been fixed by keeping the most
appropriate single category:

- **Control-specific categories** preferred (Button, Entry,
CollectionView, ListView, etc.)
- **Feature categories** kept when no control-specific category applied
(Navigation, Shell, Gestures, etc.)
- **Removed generic categories** like `Compatibility` in favor of more
specific ones

### Added Analyzer Unit Tests

New test project `UITest.Analyzers.Tests` with 20 tests covering:
- Missing category detection
- Multiple categories detection
- Custom `CategoryAttribute` derivatives
- Platform ignore attribute exclusions
- Edge cases

## CI Impact

Tests with multiple categories were running multiple times because the
CI uses category-based test filtering with OR logic. For example, a test
with `[Category("Button")]` and `[Category("Compatibility")]` would run
in both the Button job and the Compatibility job.

**Before this PR:**
- ~314 tests had multiple categories
- Each ran 2-3x per platform depending on category count
- Estimated **~1,256 duplicate test executions** per CI run (314 × ~1
extra × 4 platforms)

**After this PR:**
- Every test runs exactly once per platform
- Build fails if a developer adds multiple categories
- Significant CI time savings

## Testing

- [x] All 314 affected tests updated
- [x] Test project builds successfully with 0 errors
- [x] Analyzer unit tests pass (20/20)
- [x] Verified analyzer catches both missing and multiple category
violations

---------

Co-authored-by: Copilot <198982749+Copilot@users.noreply.github.com>
Co-authored-by: rmarinho <1235097+rmarinho@users.noreply.github.com>
Co-authored-by: Shane Neuville <shneuvil@microsoft.com>
This pull request updates the documentation comments for two core
controls, `ActivityIndicator` and `Border`, by replacing `<include>`
tags with explicit XML documentation comments. This makes the
documentation self-contained and easier to maintain. Additionally, the
PR removes the legacy XML documentation files for `ContentView` and
`ProgressBar`, likely as part of a broader move away from external XML
doc files.

**Documentation improvements for controls:**

* Replaced `<include>` tags with explicit XML documentation comments for
the `ActivityIndicator` class and its key properties and constructor,
providing clear inline summaries and remarks.
[[1]](diffhunk://#diff-c044c2b011c27c4ffdd80e3c5c18bc2d220916e74493284c4adc46cb42654c80L9-R14)
[[2]](diffhunk://#diff-c044c2b011c27c4ffdd80e3c5c18bc2d220916e74493284c4adc46cb42654c80L21-R47)
* Added comprehensive XML documentation comments to the `Border` class
and its `Content` and `Padding` properties, describing their purpose and
usage.
[[1]](diffhunk://#diff-356d119e99e7b0473adc38b35839aff1ab325ba60796b0f4774e549b47400d4aR12-R18)
[[2]](diffhunk://#diff-356d119e99e7b0473adc38b35839aff1ab325ba60796b0f4774e549b47400d4aR45-R56)

**Removal of legacy documentation files:**

* Deleted the XML documentation file for `ContentView`, removing all
related type and member documentation.
* Deleted the XML documentation file for `ProgressBar`, removing all
related type and member documentation.

---------

Co-authored-by: Shane Neuville <shneuvil@microsoft.com>
#33214)

<!-- 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 two XAML SourceGen issues reported:

1. **"Cannot create an instance of the abstract type or interface
'Brush'"** - When `OnPlatform<Brush>` was used with
`<OnPlatform.Default>` syntax and no matching platform.

2. **"error CS0122: 'View.View()' is inaccessible due to its protection
level"** - When `OnPlatform<View>` was used and no matching platform.

### Example XAML that was failing:

```xml
<OnPlatform x:Key="RadEntryInvalidBorderBrush" x:TypeArguments="Brush">
    <On Platform="WinUI">
        <LinearGradientBrush StartPoint="0.5,0" EndPoint="0.5,1">
            <LinearGradientBrush.GradientStops>
                <GradientStop Offset="0.00" Color="{StaticResource RadOnAppSurfaceColorAlpha6}" />
                <GradientStop Offset="0.93" Color="{StaticResource RadOnAppSurfaceColorAlpha6}" />
                <GradientStop Offset="0.93" Color="{StaticResource RadErrorColor}" />
                <GradientStop Offset="1.00" Color="{StaticResource RadErrorColor}" />
            </LinearGradientBrush.GradientStops>
        </LinearGradientBrush>
    </On>
    <OnPlatform.Default>
        <SolidColorBrush Color="{StaticResource RadErrorColor}" />
    </OnPlatform.Default>
</OnPlatform>

<OnPlatform x:TypeArguments="View">
    <On Platform="WinUI">
        <telerikMauiControls:RadBorder Grid.Row="2"
                                        BackgroundColor="{DynamicResource RadAIPromptInputViewFooterBackgroundColor}" />
    </On>
</OnPlatform>
```

## Root Cause

Two issues in `SimplifyOnPlatformVisitor.cs`:

1. `GetDefault()` only checked for `Default` property with empty
namespace (`new XmlName("", "Default")`), but when using
`<OnPlatform.Default>` element syntax, the property is stored with the
MAUI namespace URI.

2. When no matching platform and no Default was found, the code created
an `ElementNode` from the `x:TypeArguments` type, which later failed
when `CreateValuesVisitor` tried to instantiate abstract types or types
with protected constructors.

## Fix

1. Updated `GetDefault()` to check both empty namespace (attribute
syntax) and MAUI namespace (element syntax `<OnPlatform.Default>`)

2. When no matching platform AND no Default is found, skip
simplification and keep the `OnPlatform` element for runtime resolution
instead of trying to create a default value. This is the safe approach
because the runtime `OnPlatform<T>` correctly returns `default(T)`.

## Tests Added

Added 6 new tests in `OnPlatformAbstractTypes.cs` covering:
- `OnPlatformWithAbstractBrushTypeUsesDefault` - Brush with Default on
non-matching platform
- `OnPlatformWithAbstractBrushTypeMatchesPlatform` - Brush when platform
matches
- `OnPlatformWithViewTypeUsesDefault` - View with Default on
non-matching platform
- `OnPlatformWithViewTypeMatchesPlatform` - View when platform matches
- `OnPlatformWithAbstractTypeNoDefaultNoMatchingPlatform` - Brush
without Default on non-matching platform
- `OnPlatformWithProtectedCtorTypeNoDefaultNoMatchingPlatform` - View
without Default on non-matching platform
<!-- 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

Updates the investigations documentation.

### Issues Fixed

N/A - Documentation update.

<!-- START COPILOT CODING AGENT SUFFIX -->



<details>

<summary>Original prompt</summary>

Can you create a PR for a custom agent we can use for triaging? Please
just use the following specifications for the custom agent to start and
then we can iterate


---
name: "triage-agent"
description: "Custom agent for performing dotnet/maui repository triage
using live GitHub data without requiring authentication"
tools:
  - browser
---

# MAUI Triage Agent

You are a specialized triage agent for the dotnet/maui repository. Your
role is to help users perform triage tasks by navigating to **live
GitHub search results** in the browser - **no GitHub token required**.

## Purpose

This agent performs triage operations on the dotnet/maui repository by
using the browser to access live GitHub search results. All data is
retrieved in real-time from GitHub's public web interface without any
authentication.

## How It Works

This agent uses the **browser tool** to:
1. Navigate to GitHub search URLs that return live, real-time data
2. Read and analyze the search results directly from the GitHub web
interface
3. Provide triage recommendations based on current repository state

**No local files, no pre-generated data, no authentication required.**

## Triage Categories and Live URLs

### 1. 🚨 Untriaged Issues (HIGH PRIORITY)
**Description**: Open issues with no milestone that need triage
attention.

**Live URL**:
```
https://github.com/dotnet/maui/issues?q=is%3Aopen+is%3Aissue+no%3Amilestone+-label%3As%2Fneeds-info+-label%3As%2Fneeds-repro+-label%3As%2Ftry-latest-version+-label%3As%2Fmove-to-vs-feedback
```

**Search Query**: `is:open is:issue no:milestone -label:s/needs-info
-label:s/needs-repro -label:s/try-latest-version
-label:s/move-to-vs-feedback`

**Criteria**:
- State: open
- Milestone: none/empty
- Excluded labels: s/needs-info, s/needs-repro, s/try-latest-version,
s/move-to-vs-feedback

---

### 2. 🆕 Community PRs - No Feedback (HIGH PRIORITY)
**Description**: Community PRs that have not received any feedback from
team members.

**Live URL**:
```
https://github.com/dotnet/maui/pulls?q=is%3Aopen+is%3Apr+label%3A%22community+%E2%9C%A8%22+-is%3Adraft+comments%3A0
```

**Search Query**: `is:open is:pr label:"community ✨" -is:draft
comments:0`

**Criteria**:
- Has "community ✨" label
- Not a draft
- No comments yet

---

### 3. 🆕 All Community PRs
**Description**: All open community PRs needing attention.

**Live URL**:
```
https://github.com/dotnet/maui/pulls?q=is%3Aopen+is%3Apr+label%3A%22community+%E2%9C%A8%22+-is%3Adraft
```

**Search Query**: `is:open is:pr label:"community ✨" -is:draft`

---

### 4. ✅ Approved PRs Awaiting Action
**Description**: PRs that have been approved by reviewers and may be
ready to merge.

**Live URL**:
```
https://github.com/dotnet/maui/pulls?q=is%3Aopen+is%3Apr+review%3Aapproved
```

**Search Query**: `is:open is:pr review:approved`

---

### 5. 🤖 Copilot PRs
**Description**: PRs created by GitHub Copilot.

**Live URL**:
```
https://github.com/dotnet/maui/pulls?q=is%3Aopen+is%3Apr+author%3Acopilot
```

**Search Query**: `is:open is:pr author:copilot`

---

### 6. 🔧 Candidate Branch PRs
**Description**: PRs targeting inflight/candidate branches.

**Live URL (targeting candidate)**:
```
https://github.com/dotnet/maui/pulls?q=is%3Aopen+is%3Apr+base%3Ainflight%2Fcandidate
```

**Live URL (targeting current)**:
```
https://github.com/dotnet/maui/pulls?q=is%3Aopen+is%3Apr+base%3Ainflight%2Fcurrent
```

**Search Query**: `is:open is:pr base:inflight/candidate` or `is:open
is:pr base:inflight/current`

---

### 7. �� PRs with GA/SR Milestones
**Description**: PRs assigned to GA or Service Release milestones.

**Live URL (SR milestones)**:
```
https://github.com/dotnet/maui/pulls?q=is%3Aopen+is%3Apr+milestone%3A%22.NET+10.0+SR2%22
```

**Live URL (GA milestones)**:
```
https://github.com/dotnet/maui/pulls?q=is%3Aopen+is%3Apr+milestone%3A%22.NET+10.0+GA%22
```

**Note**: Adjust milestone names based on current release cycle.

---

### 8. 📋 High Priority Issues (p/0)
**Description**: Issues marked as highest priority.

**Live URL**:
```
https://github.com/dotnet/maui/issues?q=is%3Aopen+is%3Aissue+label%3Ap%2F0
```

**Search Query**: `is:open is:issue label:p/0`

---

### 9. 📋 Regression Issues
**Description**: Issues that are regressions from previous behavior.

**Live URL**:
```
https://github.com/dotnet/maui/issues?q=is%3Aopen+is%3Aissue+label%3Ai%2Fregression
```

**Search Query**: `is:open is:issue label:i/regression`

---

## How to Perform Triage

### Step-by-Step Process

1. **Navigate to the URL**: Use the browser tool to navigate to the
relevant GitHub search URL
2. **Read the results**: Analyze the search results page to get counts
and issue/PR details
3. **Report findings**: Summarize what you found with counts, titles,
and recommendations
4. **Provide links**: Always include the live URL so users can view
results directly

### Example Triage Session

**User**: "Help me triage dotnet/maui"

**Agent Actions**:
1. Navigate to untriaged issues URL
2. Count and summarize results
3...

</details>



<!-- START COPILOT CODING AGENT TIPS -->
---

💬 We'd love your input! Share your thoughts on Copilot coding agent in
our [2 minute survey](https://gh.io/copilot-coding-agent-survey).

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: PureWeen <5375137+PureWeen@users.noreply.github.com>
Co-authored-by: Shane Neuville <shneuvil@microsoft.com>
@PureWeen
Copy link
Member Author

PureWeen commented Jan 1, 2026

/azp run

@azure-pipelines
Copy link

Azure Pipelines successfully started running 3 pipeline(s).

@rmarinho
Copy link
Member

rmarinho commented Jan 5, 2026

/azp run maui-pr-devicetests

@azure-pipelines
Copy link

Azure Pipelines successfully started running 1 pipeline(s).

@rmarinho
Copy link
Member

rmarinho commented Jan 5, 2026

/azp run maui-pr-uitests

@azure-pipelines
Copy link

Azure Pipelines successfully started running 1 pipeline(s).

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.