Skip to content

LineHeight and decorations for HTML Label - fix#31202

Merged
kubaflo merged 6 commits intodotnet:inflight/currentfrom
kubaflo:fix-22197
Apr 2, 2026
Merged

LineHeight and decorations for HTML Label - fix#31202
kubaflo merged 6 commits intodotnet:inflight/currentfrom
kubaflo:fix-22197

Conversation

@kubaflo
Copy link
Copy Markdown
Contributor

@kubaflo kubaflo commented Aug 16, 2025

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

Corrects mapping logic for LineHeight, TextDecorations, and CharacterSpacing to apply to HTML labels. Updates iOS handler to refresh these properties when TextType changes. Adds test case for Issue22197 to verify correct rendering of HTML labels with these properties.

Issues Fixed

Fixes #22193
Fixes #22197

Before After
Before After

Copilot AI review requested due to automatic review settings August 16, 2025 23:39
@kubaflo kubaflo requested a review from a team as a code owner August 16, 2025 23:39
@kubaflo kubaflo self-assigned this Aug 16, 2025
@dotnet-policy-service dotnet-policy-service bot added the community ✨ Community Contribution label Aug 16, 2025
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

This PR fixes issues with LineHeight, TextDecorations, and CharacterSpacing not being applied to HTML labels in .NET MAUI. The fix corrects the mapping logic to allow these properties to work with HTML text type labels, not just plain text labels.

  • Updates the mapper conditions to check for FormattedTextSpans instead of plain text, allowing HTML labels to receive these style properties
  • Modifies the iOS handler to refresh LineHeight, TextDecorations, and CharacterSpacing when TextType changes
  • Adds comprehensive test case to verify the fix works correctly

Reviewed Changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
src/Controls/src/Core/Label/Label.Mapper.cs Updates mapping conditions from IsPlainText check to HasFormattedTextSpans check for LineHeight, TextDecorations, and CharacterSpacing
src/Controls/src/Core/Label/Label.iOS.cs Adds refresh calls for LineHeight, TextDecorations, and CharacterSpacing when TextType changes in iOS handler
src/Controls/tests/TestCases.HostApp/Issues/Issue22197.xaml Creates UI test page demonstrating HTML vs plain text labels with styling properties
src/Controls/tests/TestCases.HostApp/Issues/Issue22197.xaml.cs Code-behind for the test page
src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue22197.cs NUnit test implementation to verify the fix through screenshot verification

LineHeight="2">
</Label>
<Label
AutomationId="label"
Copy link

Copilot AI Aug 16, 2025

Choose a reason for hiding this comment

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

The AutomationId 'label' is too generic and could conflict with other elements. Consider using a more specific identifier like 'htmlTextTypeLabel' to ensure uniqueness across all test cases.

Copilot uses AI. Check for mistakes.
@dotnet-policy-service
Copy link
Copy Markdown
Contributor

Hey there @@kubaflo! Thank you so much for your PR! Someone from the team will get assigned to your PR shortly and we'll get it reviewed.

@kubaflo kubaflo added platform/android platform/ios area-controls-label Label, Span and removed community ✨ Community Contribution labels Aug 16, 2025
@kubaflo kubaflo changed the title Fix LineHeight and decorations for HTML Label LineHeight and decorations for HTML Label - fix Aug 16, 2025
@MartyIX
Copy link
Copy Markdown
Contributor

MartyIX commented Aug 17, 2025

This is how it looks like on Windows:

image

(For reference purposes)

LineHeight="2"
Text="Etiam sodales sollicitudin diam, vel tincidunt libero eleifend id. Vestibulum vehicula congue velit, id egestas nulla pellentesque at."/>
<Label Text="Plain Text type"/>
</VerticalStackLayout>
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This would exercise the line height feature more:

Suggested change
</VerticalStackLayout>
<!-- Exercise line heights less than 1. -->
<Label BackgroundColor="Green" FontSize="20" Text="ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ"/>
<Label BackgroundColor="Red" LineHeight="0.8" FontSize="20" Text="ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ"/>
<Label BackgroundColor="Green" LineHeight="0.2" FontSize="20" Text="ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ"/>
<Label BackgroundColor="Red" LineHeight="0.4" FontSize="20" Text="ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ"/>
<Label BackgroundColor="Green" LineHeight="1.0" FontSize="20" Text="ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ"/>
<Label BackgroundColor="Red" LineHeight="1.2" FontSize="20" Text="ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ"/>
<Label BackgroundColor="Green" LineHeight="1.4" FontSize="20" Text="ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ"/>
<Label BackgroundColor="Red" LineHeight="1.6" FontSize="20" Text="ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ"/>
<Label BackgroundColor="Green" LineHeight="2.0" FontSize="20" Text="ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ"/>
<Label BackgroundColor="Red" LineHeight="4.6" FontSize="20" Text="ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ"/>
</VerticalStackLayout>

See comment #24520 (comment)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Hmmm but this pr is not about lineHeight in this sense. It is about the line Height for HTML labels

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Good point. I moved the comment to #31219.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Mar 16, 2026

🚀 Dogfood this PR with:

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

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

Or

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

PureWeen and others added 5 commits March 25, 2026 09:44
…otnet#34548)

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

Adds a [gh-aw (GitHub Agentic
Workflows)](https://github.github.com/gh-aw/introduction/overview/)
workflow that automatically evaluates test quality on PRs using the
`evaluate-pr-tests` skill.

### What it does

When a PR adds or modifies test files, this workflow:
1. **Checks out the PR branch** (including fork PRs) in a pre-agent step
2. **Runs the `evaluate-pr-tests` skill** via Copilot CLI in a sandboxed
container
3. **Posts the evaluation report** as a PR comment using gh-aw
safe-outputs

### Triggers

| Trigger | When | Fork PR support |
|---------|------|-----------------|
| `pull_request` | Automatic on test file changes (`src/**/tests/**`) |
❌ Blocked by `pre_activation` gate |
| `workflow_dispatch` | Manual — enter PR number | ✅ Works for all PRs |
| `issue_comment` (`/evaluate-tests`) | Comment on PR | ⚠️ Same-repo
only (see Known Limitations) |

### Security model

| Layer | Implementation |
|-------|---------------|
| **gh-aw sandbox** | Agent runs in container with scrubbed credentials,
network firewall |
| **Safe outputs** | Max 1 PR comment per run, content-limited |
| **Checkout without execution** | `steps:` checks out PR code but never
executes workspace scripts |
| **Base branch restoration** | `.github/skills/`,
`.github/instructions/`, `.github/copilot-instructions.md` restored from
base branch after checkout |
| **Fork PR activation gate** | `pull_request` events blocked for forks
via `head.repo.id == repository_id` |
| **Pinned actions** | SHA-pinned `actions/checkout`,
`actions/github-script`, etc. |
| **Minimal permissions** | Each job declares only what it needs |
| **Concurrency** | One evaluation per PR, cancels in-progress |
| **Threat detection** | gh-aw built-in threat detection analyzes agent
output |

### Files added/modified

- `.github/workflows/copilot-evaluate-tests.md` — gh-aw workflow source
- `.github/workflows/copilot-evaluate-tests.lock.yml` — Compiled
workflow (auto-generated by `gh aw compile`)
- `.github/skills/evaluate-pr-tests/scripts/Gather-TestContext.ps1` —
Test context gathering script (binary-safe file download, path traversal
protection)
- `.github/instructions/gh-aw-workflows.instructions.md` — Copilot
instructions for gh-aw development

### Known Limitations

**Fork PR evaluation via `/evaluate-tests` comment is not supported in
v1.** The gh-aw platform inserts a `checkout_pr_branch.cjs` step after
all user steps, which may overwrite base-branch skill files restored for
fork PRs. This is a known gh-aw platform limitation — user steps always
run before platform-generated steps, with no way to insert steps after.

**Workaround:** Use `workflow_dispatch` (Actions UI → "Run workflow" →
enter PR number) to evaluate fork PRs. This trigger bypasses the
platform checkout step entirely and works correctly.

**Related upstream issues:**
- [github/gh-aw#18481](github/gh-aw#18481) —
"Using gh-aw in forks of repositories"
- [github/gh-aw#18518](github/gh-aw#18518) —
Fork detection and warning in `gh aw init`
- [github/gh-aw#18520](github/gh-aw#18520) —
Fork context hint in failure messages
- [github/gh-aw#18521](github/gh-aw#18521) —
Fork support documentation

### Fixes

- Fixes dotnet#34602

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Jakub Florkowski <kubaflo123@gmail.com>
## Summary

Enables the copilot-evaluate-tests gh-aw workflow to run on fork PRs by
adding `forks: ["*"]` to the `pull_request` trigger and removing the
fork guard from `Checkout-GhAwPr.ps1`.

## Changes

1. **copilot-evaluate-tests.md**: Added `forks: ["*"]` to opt out of
gh-aw auto-injected fork activation guard. Scoped `Checkout-GhAwPr.ps1`
step to `workflow_dispatch` only (redundant for other triggers since
platform handles checkout).

2. **copilot-evaluate-tests.lock.yml**: Recompiled via `gh aw compile` —
fork guard removed from activation `if:` conditions.

3. **Checkout-GhAwPr.ps1**: Removed the `isCrossRepository` fork guard.
Updated header docs and restore comments to accurately describe behavior
for all trigger×fork combinations (including corrected step ordering).

4. **gh-aw-workflows.instructions.md**: Updated all stale references to
the removed fork guard. Documented `forks: ["*"]` opt-in, clarified
residual risk model for fork PRs, and updated troubleshooting table.

## Security Model

Fork PRs are safe because:
- Agent runs in **sandboxed container** with all credentials scrubbed
- Output limited to **1 comment** via `safe-outputs: add-comment: max:
1`
- Agent **prompt comes from base branch** (`runtime-import`) — forks
cannot alter instructions
- Pre-flight check catches missing `SKILL.md` if fork isn't rebased on
`main`
- No workspace code is executed with `GITHUB_TOKEN` (checkout without
execution)

## Testing

- ✅ `workflow_dispatch` tested against fork PR dotnet#34621
- ✅ Lock.yml statically verified — fork guard removed from `if:`
conditions
- ⏳ `pull_request` trigger on fork PRs can only be verified post-merge
(GitHub Actions reads lock.yml from default branch)

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…taType is compiled (dotnet#34717)

## Description

Adds regression tests for dotnet#34713 verifying the XAML source generator
correctly handles bindings with `Converter={StaticResource ...}` inside
`x:DataType` scopes.

Closes dotnet#34713

## Investigation

After thorough investigation of the source generator pipeline
(`KnownMarkups.cs`, `CompiledBindingMarkup.cs`, `NodeSGExtensions.cs`):

### When converter IS in page resources (compile-time resolution ✅)

`GetResourceNode()` walks the XAML tree, finds the converter resource,
and `ProvideValueForStaticResourceExtension` returns the variable
directly — **no runtime `ProvideValue` call**. The converter is
referenced at compile time.

### When converter is NOT in page resources (runtime resolution ✅)

`GetResourceNode()` returns null → falls through to `IsValueProvider` →
generates `StaticResourceExtension.ProvideValue(serviceProvider)`. The
`SimpleValueTargetProvider` provides the full parent chain, and
`TryGetApplicationLevelResource` checks `Application.Current.Resources`.
The binding IS still compiled into a `TypedBinding` — only the converter
resolution is deferred.

### Verified on both `main` and `net11.0`

All tests pass on both branches.

## Tests added

| Test | What it verifies |
|------|-----------------|
| `SourceGenResolvesConverterAtCompileTime_ImplicitResources` |
Converter in implicit `<Resources>` → compile-time resolution, no
`ProvideValue` |
| `SourceGenResolvesConverterAtCompileTime_ExplicitResourceDictionary` |
Converter in explicit `<ResourceDictionary>` → compile-time resolution,
no `ProvideValue` |
| `SourceGenCompilesBindingWithConverterToTypedBinding` | Converter NOT
in page resources → still compiled to `TypedBinding`, no raw `Binding`
fallback |
| `BindingWithConverterFromAppResourcesWorksCorrectly` × 3 | Runtime
behavior correct for all inflators (Runtime, XamlC, SourceGen) |

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

## Description

Adds arcade inter-branch merge workflow and configuration to automate
merging `net11.0` into the `release/11.0.1xx-preview3` branch.

### Files added

| File | Purpose |
|------|---------|
| `github-merge-flow-release-11.jsonc` | Merge flow config — source
`net11.0`, target `release/11.0.1xx-preview3` |
| `.github/workflows/merge-net11-to-release.yml` | GitHub Actions
workflow — triggers on push to net11.0, daily cron, manual dispatch |

### How it works

Uses the shared [dotnet/arcade inter-branch merge
infrastructure](https://github.com/dotnet/arcade/blob/main/.github/workflows/inter-branch-merge-base.yml):
- **Event-driven**: triggers on push to `net11.0`, with daily cron
safety net
- **ResetToTargetPaths**: auto-resets `global.json`, `NuGet.config`,
`eng/Version.Details.xml`, `eng/Versions.props`, `eng/common/*` to
target branch versions
- **QuietComments**: reduces GitHub notification noise
- Skips PRs when only Maestro bot commits exist

### Incrementing for future releases

When cutting a new release (e.g., preview4), update:
1. `github-merge-flow-release-11.jsonc` → change `MergeToBranch` value
2. `.github/workflows/merge-net11-to-release.yml` → update workflow
`name` field

Follows the same pattern as `merge-main-to-net11.yml` /
`github-merge-flow-net11.jsonc`.

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Corrects mapping logic for LineHeight, TextDecorations, and CharacterSpacing to apply to HTML labels. Updates iOS handler to refresh these properties when TextType changes. Adds test case for Issue22197 to verify correct rendering of HTML labels with these properties.
@kubaflo
Copy link
Copy Markdown
Contributor Author

kubaflo commented Mar 31, 2026

/azp run maui-pr-uitests

@azure-pipelines
Copy link
Copy Markdown

Azure Pipelines successfully started running 1 pipeline(s).

@kubaflo
Copy link
Copy Markdown
Contributor Author

kubaflo commented Apr 1, 2026

Code Review — PR #31202

Independent Assessment

What this changes: Corrects the mapper guard logic for LineHeight, TextDecorations, and CharacterSpacing on Label (Android + iOS). Previously these three properties were blocked for any non-plain-text label — including HTML labels — by the !IsPlainText(label) check. This PR narrows the guard to label.HasFormattedTextSpans, so the properties now correctly apply to HTML labels (TextType=Html) while still being skipped for FormattedText with spans. On iOS, MapFormatting is extended to also refresh these three properties when text type is HTML, since iOS attributed strings get rebuilt on text change.

Inferred motivation: Users setting LineHeight, TextDecorations, or CharacterSpacing on HTML labels see those properties silently ignored. The root cause is an overly broad guard that conflated "has formatted spans" with "is HTML."

Reconciliation with PR Narrative

Author claims: Fixes #22193 and #22197 — LineHeight/TextDecorations/CharacterSpacing not applied to HTML labels.
Agreement: Full agreement. The code does exactly what's described. The guard change is minimal, targeted, and logically correct.

Findings

⚠️ Stale comments — two comments now contradict the new behavior

src/Controls/src/Core/Label/Label.Mapper.cs:38 and :114 both still say:

// these are for properties that should only apply to plain text (not spans nor html)

After this change, these properties now do apply to HTML. Suggest updating to something like:

// these are for properties that should not apply to formatted text (spans)

⚠️ Missing newlines at end of file — all 3 new test files

Issue22197.xaml, Issue22197.xaml.cs, and the NUnit test Issue22197.cs all lack a trailing newline (\ No newline at end of file in the diff). Most files in the repo end with one.

💡 CI — baseline snapshots needed

The LineHeightWithHTMLLabelShouldWork screenshot test fails on all platforms with "Baseline snapshot not yet created." This is expected — the generated snapshot needs to be reviewed and committed. The other Label-lane failures (Issue22469, Issue23491, Issue23801) are pre-existing timeouts unrelated to this PR.

Devil's Advocate

  • Could the mapper change break existing HTML labels that previously ignored these properties? In theory yes — an HTML label that happened to have LineHeight, TextDecorations, or CharacterSpacing set but relied on them being silently ignored would now render differently. In practice this is a bug fix: users setting those properties expect them to take effect, and the old behavior was the bug.
  • Is the iOS MapFormatting change sufficient without a matching Android change? Yes. On Android, LineHeight/TextDecorations/CharacterSpacing are view-level properties (setLineSpacing, paint flags, letter spacing) that persist across text content changes. On iOS, they're encoded as NSAttributedString attributes that get rebuilt when HTML text is set, so they must be re-applied — hence the MapFormatting addition is iOS-only by design.

Verdict: LGTM

Confidence: high
Summary: The core logic change is correct and well-scoped — narrowing the guard from IsPlainText to HasFormattedTextSpans is the right fix. The iOS MapFormatting extension is necessary for the iOS attributed-string model. Two stale code comments and missing trailing newlines in the test files should be cleaned up before merge, and the screenshot baselines need to be generated from CI artifacts.

@kubaflo
Copy link
Copy Markdown
Contributor Author

kubaflo commented Apr 1, 2026

🟢 .NET MAUI Review - Approved

Expand Full Review - 6242a19 - LineHeight and decorations for HTML Label - fix

Independent Assessment

What this changes: Corrects the mapper guard logic for LineHeight, TextDecorations, and CharacterSpacing on Label (Android + iOS). Previously these three properties were blocked for any non-plain-text label — including HTML labels — by the !IsPlainText(label) check. This PR narrows the guard to label.HasFormattedTextSpans, so the properties now correctly apply to HTML labels (TextType=Html) while still being skipped for FormattedText with spans. On iOS, MapFormatting is extended to also refresh these three properties when text type is HTML, since iOS attributed strings get rebuilt on text change.

Inferred motivation: Users setting LineHeight, TextDecorations, or CharacterSpacing on HTML labels see those properties silently ignored. The root cause is an overly broad guard that conflated "has formatted spans" with "is HTML."

Reconciliation with PR Narrative

Author claims: Fixes #22193 and #22197 — LineHeight/TextDecorations/CharacterSpacing not applied to HTML labels.
Agreement: Full agreement. The code does exactly what's described. The guard change is minimal, targeted, and logically correct.

Findings

⚠️ Stale comments — two comments now contradict the new behavior

src/Controls/src/Core/Label/Label.Mapper.cs:38 and :114 both still say:

// these are for properties that should only apply to plain text (not spans nor html)

After this change, these properties now do apply to HTML. Suggest updating to something like:

// these are for properties that should not apply to formatted text (spans)

⚠️ Missing newlines at end of file — all 3 new test files

Issue22197.xaml, Issue22197.xaml.cs, and the NUnit test Issue22197.cs all lack a trailing newline (\ No newline at end of file in the diff). Most files in the repo end with one.

💡 CI — baseline snapshots needed

The LineHeightWithHTMLLabelShouldWork screenshot test fails on all platforms with "Baseline snapshot not yet created." This is expected — the generated snapshot needs to be reviewed and committed. The other Label-lane failures (Issue22469, Issue23491, Issue23801) are pre-existing timeouts unrelated to this PR.

Devil's Advocate

  • Could the mapper change break existing HTML labels that previously ignored these properties? In theory yes — an HTML label that happened to have LineHeight, TextDecorations, or CharacterSpacing set but relied on them being silently ignored would now render differently. In practice this is a bug fix: users setting those properties expect them to take effect, and the old behavior was the bug.
  • Is the iOS MapFormatting change sufficient without a matching Android change? Yes. On Android, LineHeight/TextDecorations/CharacterSpacing are view-level properties (setLineSpacing, paint flags, letter spacing) that persist across text content changes. On iOS, they're encoded as NSAttributedString attributes that get rebuilt when HTML text is set, so they must be re-applied — hence the MapFormatting addition is iOS-only by design.

Verdict: LGTM

Confidence: high
Summary: The core logic change is correct and well-scoped — narrowing the guard from IsPlainText to HasFormattedTextSpans is the right fix. The iOS MapFormatting extension is necessary for the iOS attributed-string model. Two stale code comments and missing trailing newlines in the test files should be cleaned up before merge, and the screenshot baselines need to be generated from CI artifacts.

@MauiBot
Copy link
Copy Markdown
Collaborator

MauiBot commented Apr 1, 2026

🚦 Gate - Test Before and After Fix

📊 Expand Full Gate85c88f7 · Snapshots

Gate Result: ✅ PASSED

Platform: ANDROID · Base: main · Merge base: 720a9d4a

Test Without Fix (expect FAIL) With Fix (expect PASS)
🖥️ Issue22197 Issue22197 ✅ FAIL — 671s ✅ PASS — 511s
🔴 Without fix — 🖥️ Issue22197: FAIL ✅ · 671s
  Determining projects to restore...
  Restored /home/vsts/work/1/s/src/BlazorWebView/src/Maui/Microsoft.AspNetCore.Components.WebView.Maui.csproj (in 9.37 sec).
  Restored /home/vsts/work/1/s/src/Controls/src/Core/Controls.Core.csproj (in 9.32 sec).
  Restored /home/vsts/work/1/s/src/Controls/Foldable/src/Controls.Foldable.csproj (in 456 ms).
  Restored /home/vsts/work/1/s/src/Graphics/src/Graphics/Graphics.csproj (in 29 ms).
  Restored /home/vsts/work/1/s/src/Essentials/src/Essentials.csproj (in 34 ms).
  Restored /home/vsts/work/1/s/src/Core/src/Core.csproj (in 90 ms).
  Restored /home/vsts/work/1/s/src/Core/maps/src/Maps.csproj (in 42 ms).
  Restored /home/vsts/work/1/s/src/Controls/Maps/src/Controls.Maps.csproj (in 907 ms).
  Restored /home/vsts/work/1/s/src/Controls/src/Xaml/Controls.Xaml.csproj (in 55 ms).
  Restored /home/vsts/work/1/s/src/Controls/tests/TestCases.HostApp/Controls.TestCases.HostApp.csproj (in 534 ms).
  1 of 11 projects are up-to-date for restore.
  ##vso[build.updatebuildnumber]10.0.60-ci+azdo.13717716
  Graphics -> /home/vsts/work/1/s/artifacts/bin/Graphics/Debug/net10.0-android36.0/Microsoft.Maui.Graphics.dll
  ##vso[build.updatebuildnumber]10.0.60-ci+azdo.13717716
  Essentials -> /home/vsts/work/1/s/artifacts/bin/Essentials/Debug/net10.0-android36.0/Microsoft.Maui.Essentials.dll
  ##vso[build.updatebuildnumber]10.0.60-ci+azdo.13717716
  Core -> /home/vsts/work/1/s/artifacts/bin/Core/Debug/net10.0-android36.0/Microsoft.Maui.dll
  Controls.BindingSourceGen -> /home/vsts/work/1/s/artifacts/bin/Controls.BindingSourceGen/Debug/netstandard2.0/Microsoft.Maui.Controls.BindingSourceGen.dll
  ##vso[build.updatebuildnumber]10.0.60-ci+azdo.13717716
  ##vso[build.updatebuildnumber]10.0.60-ci+azdo.13717716
  Maps -> /home/vsts/work/1/s/artifacts/bin/Maps/Debug/net10.0-android36.0/Microsoft.Maui.Maps.dll
  Controls.Core -> /home/vsts/work/1/s/artifacts/bin/Controls.Core/Debug/net10.0-android36.0/Microsoft.Maui.Controls.dll
  ##vso[build.updatebuildnumber]10.0.60-ci+azdo.13717716
  ##vso[build.updatebuildnumber]10.0.60-ci+azdo.13717716
  ##vso[build.updatebuildnumber]10.0.60-ci+azdo.13717716
  Controls.Foldable -> /home/vsts/work/1/s/artifacts/bin/Controls.Foldable/Debug/net10.0-android36.0/Microsoft.Maui.Controls.Foldable.dll
  Microsoft.AspNetCore.Components.WebView.Maui -> /home/vsts/work/1/s/artifacts/bin/Microsoft.AspNetCore.Components.WebView.Maui/Debug/net10.0-android36.0/Microsoft.AspNetCore.Components.WebView.Maui.dll
  Controls.Maps -> /home/vsts/work/1/s/artifacts/bin/Controls.Maps/Debug/net10.0-android36.0/Microsoft.Maui.Controls.Maps.dll
  ##vso[build.updatebuildnumber]10.0.60-ci+azdo.13717716
  Controls.Xaml -> /home/vsts/work/1/s/artifacts/bin/Controls.Xaml/Debug/net10.0-android36.0/Microsoft.Maui.Controls.Xaml.dll
  Controls.TestCases.HostApp -> /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.HostApp/Debug/net10.0-android/Controls.TestCases.HostApp.dll
  ##vso[build.updatebuildnumber]10.0.60-ci+azdo.13717716
  Graphics -> /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.HostApp/Debug/net10.0-android/Microsoft.Maui.Graphics.dll
  ##vso[build.updatebuildnumber]10.0.60-ci+azdo.13717716
  Essentials -> /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.HostApp/Debug/net10.0-android/Microsoft.Maui.Essentials.dll
  ##vso[build.updatebuildnumber]10.0.60-ci+azdo.13717716
  Core -> /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.HostApp/Debug/net10.0-android/Microsoft.Maui.dll
  ##vso[build.updatebuildnumber]10.0.60-ci+azdo.13717716
  Controls.BindingSourceGen -> /home/vsts/work/1/s/artifacts/bin/Controls.BindingSourceGen/Debug/netstandard2.0/Microsoft.Maui.Controls.BindingSourceGen.dll
  Maps -> /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.HostApp/Debug/net10.0-android/Microsoft.Maui.Maps.dll
  ##vso[build.updatebuildnumber]10.0.60-ci+azdo.13717716
  Controls.Core -> /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.HostApp/Debug/net10.0-android/Microsoft.Maui.Controls.dll
  ##vso[build.updatebuildnumber]10.0.60-ci+azdo.13717716
  ##vso[build.updatebuildnumber]10.0.60-ci+azdo.13717716
  ##vso[build.updatebuildnumber]10.0.60-ci+azdo.13717716
  ##vso[build.updatebuildnumber]10.0.60-ci+azdo.13717716
  Controls.Foldable -> /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.HostApp/Debug/net10.0-android/Microsoft.Maui.Controls.Foldable.dll
  Controls.Xaml -> /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.HostApp/Debug/net10.0-android/Microsoft.Maui.Controls.Xaml.dll
  Controls.Maps -> /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.HostApp/Debug/net10.0-android/Microsoft.Maui.Controls.Maps.dll
  Microsoft.AspNetCore.Components.WebView.Maui -> /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.HostApp/Debug/net10.0-android/Microsoft.AspNetCore.Components.WebView.Maui.dll

Build succeeded.
    0 Warning(s)
    0 Error(s)

Time Elapsed 00:08:11.37
Broadcasting: Intent { act=android.intent.action.CLOSE_SYSTEM_DIALOGS flg=0x400000 }
Broadcast completed: result=0
Broadcasting: Intent { act=android.intent.action.CLOSE_SYSTEM_DIALOGS flg=0x400000 }
Broadcast completed: result=0
  Determining projects to restore...
  Restored /home/vsts/work/1/s/src/TestUtils/src/VisualTestUtils/VisualTestUtils.csproj (in 1.17 sec).
  Restored /home/vsts/work/1/s/src/Controls/tests/CustomAttributes/Controls.CustomAttributes.csproj (in 48 ms).
  Restored /home/vsts/work/1/s/src/TestUtils/src/VisualTestUtils.MagickNet/VisualTestUtils.MagickNet.csproj (in 5.15 sec).
  Restored /home/vsts/work/1/s/src/Controls/tests/TestCases.Android.Tests/Controls.TestCases.Android.Tests.csproj (in 6.4 sec).
  Restored /home/vsts/work/1/s/src/TestUtils/src/UITest.Core/UITest.Core.csproj (in 2 ms).
  Restored /home/vsts/work/1/s/src/TestUtils/src/UITest.Appium/UITest.Appium.csproj (in 2 ms).
  Restored /home/vsts/work/1/s/src/TestUtils/src/UITest.NUnit/UITest.NUnit.csproj (in 328 ms).
  Restored /home/vsts/work/1/s/src/TestUtils/src/UITest.Analyzers/UITest.Analyzers.csproj (in 2.07 sec).
  5 of 13 projects are up-to-date for restore.
  ##vso[build.updatebuildnumber]10.0.60-ci+azdo.13717716
  Graphics -> /home/vsts/work/1/s/artifacts/bin/Graphics/Debug/net10.0/Microsoft.Maui.Graphics.dll
  ##vso[build.updatebuildnumber]10.0.60-ci+azdo.13717716
  Essentials -> /home/vsts/work/1/s/artifacts/bin/Essentials/Debug/net10.0/Microsoft.Maui.Essentials.dll
  Controls.CustomAttributes -> /home/vsts/work/1/s/artifacts/bin/Controls.CustomAttributes/Debug/net10.0/Controls.CustomAttributes.dll
  ##vso[build.updatebuildnumber]10.0.60-ci+azdo.13717716
  Core -> /home/vsts/work/1/s/artifacts/bin/Core/Debug/net10.0/Microsoft.Maui.dll
  Controls.BindingSourceGen -> /home/vsts/work/1/s/artifacts/bin/Controls.BindingSourceGen/Debug/netstandard2.0/Microsoft.Maui.Controls.BindingSourceGen.dll
  ##vso[build.updatebuildnumber]10.0.60-ci+azdo.13717716
  Controls.Core -> /home/vsts/work/1/s/artifacts/bin/Controls.Core/Debug/net10.0/Microsoft.Maui.Controls.dll
  UITest.Core -> /home/vsts/work/1/s/artifacts/bin/UITest.Core/Debug/net10.0/UITest.Core.dll
  UITest.Appium -> /home/vsts/work/1/s/artifacts/bin/UITest.Appium/Debug/net10.0/UITest.Appium.dll
  UITest.NUnit -> /home/vsts/work/1/s/artifacts/bin/UITest.NUnit/Debug/net10.0/UITest.NUnit.dll
  VisualTestUtils -> /home/vsts/work/1/s/artifacts/bin/VisualTestUtils/Debug/netstandard2.0/VisualTestUtils.dll
  VisualTestUtils.MagickNet -> /home/vsts/work/1/s/artifacts/bin/VisualTestUtils.MagickNet/Debug/netstandard2.0/VisualTestUtils.MagickNet.dll
  UITest.Analyzers -> /home/vsts/work/1/s/artifacts/bin/UITest.Analyzers/Debug/netstandard2.0/UITest.Analyzers.dll
  Controls.TestCases.Android.Tests -> /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.Android.Tests/Debug/net10.0/Controls.TestCases.Android.Tests.dll
Test run for /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.Android.Tests/Debug/net10.0/Controls.TestCases.Android.Tests.dll (.NETCoreApp,Version=v10.0)
VSTest version 18.0.1 (x64)

Starting test execution, please wait...
A total of 1 test files matched the specified pattern.
/home/vsts/work/1/s/artifacts/bin/Controls.TestCases.Android.Tests/Debug/net10.0/Controls.TestCases.Android.Tests.dll
[xUnit.net 00:00:00.00] xUnit.net VSTest Adapter v2.8.2+699d445a1a (64-bit .NET 10.0.0)
[xUnit.net 00:00:00.10]   Discovering: Controls.TestCases.Android.Tests
[xUnit.net 00:00:00.29]   Discovered:  Controls.TestCases.Android.Tests
NUnit Adapter 4.5.0.0: Test execution started
Running selected tests in /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.Android.Tests/Debug/net10.0/Controls.TestCases.Android.Tests.dll
   NUnit3TestExecutor discovered 1 of 1 NUnit test cases using Current Discovery mode, Non-Explicit run
>>>>> 04/01/2026 22:39:01 FixtureSetup for Issue22197(Android)
>>>>> 04/01/2026 22:39:04 LineHeightWithHTMLLabelShouldWork Start
>>>>> 04/01/2026 22:39:10 LineHeightWithHTMLLabelShouldWork Stop
>>>>> 04/01/2026 22:39:10 Log types: logcat, bugreport, server
  Failed LineHeightWithHTMLLabelShouldWork [6 s]
  Error Message:
   VisualTestUtils.VisualTestFailedException : 
Snapshot different than baseline: LineHeightWithHTMLLabelShouldWork.png (6.10% difference)
If the correct baseline has changed (this isn't a a bug), then update the baseline image.
See test attachment or download the build artifacts to get the new snapshot file.

More info: https://aka.ms/visual-test-workflow

  Stack Trace:
     at VisualTestUtils.VisualRegressionTester.Fail(String message) in /_/src/TestUtils/src/VisualTestUtils/VisualRegressionTester.cs:line 162
   at VisualTestUtils.VisualRegressionTester.VerifyMatchesSnapshot(String name, ImageSnapshot actualImage, String environmentName, ITestContext testContext) in /_/src/TestUtils/src/VisualTestUtils/VisualRegressionTester.cs:line 123
   at Microsoft.Maui.TestCases.Tests.UITest.<VerifyScreenshot>g__Verify|13_0(String name, <>c__DisplayClass13_0&) in /_/src/Controls/tests/TestCases.Shared.Tests/UITest.cs:line 477
   at Microsoft.Maui.TestCases.Tests.UITest.VerifyScreenshot(String name, Nullable`1 retryDelay, Nullable`1 retryTimeout, Int32 cropLeft, Int32 cropRight, Int32 cropTop, Int32 cropBottom, Double tolerance) in /_/src/Controls/tests/TestCases.Shared.Tests/UITest.cs:line 309
   at Microsoft.Maui.TestCases.Tests.Issues.Issue22197.LineHeightWithHTMLLabelShouldWork() in /_/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue22197.cs:line 18
   at System.Reflection.MethodBaseInvoker.InterpretedInvoke_Method(Object obj, IntPtr* args)
   at System.Reflection.MethodBaseInvoker.InvokeWithNoArgs(Object obj, BindingFlags invokeAttr)

NUnit Adapter 4.5.0.0: Test execution complete

Total tests: 1
     Failed: 1
Test Run Failed.
 Total time: 32.9312 Seconds

🟢 With fix — 🖥️ Issue22197: PASS ✅ · 511s
  Determining projects to restore...
  All projects are up-to-date for restore.
  ##vso[build.updatebuildnumber]10.0.60-ci+azdo.13717716
  Graphics -> /home/vsts/work/1/s/artifacts/bin/Graphics/Debug/net10.0-android36.0/Microsoft.Maui.Graphics.dll
  ##vso[build.updatebuildnumber]10.0.60-ci+azdo.13717716
  Essentials -> /home/vsts/work/1/s/artifacts/bin/Essentials/Debug/net10.0-android36.0/Microsoft.Maui.Essentials.dll
  ##vso[build.updatebuildnumber]10.0.60-ci+azdo.13717716
  Core -> /home/vsts/work/1/s/artifacts/bin/Core/Debug/net10.0-android36.0/Microsoft.Maui.dll
  Controls.BindingSourceGen -> /home/vsts/work/1/s/artifacts/bin/Controls.BindingSourceGen/Debug/netstandard2.0/Microsoft.Maui.Controls.BindingSourceGen.dll
  ##vso[build.updatebuildnumber]10.0.60-ci+azdo.13717716
  ##vso[build.updatebuildnumber]10.0.60-ci+azdo.13717716
  Maps -> /home/vsts/work/1/s/artifacts/bin/Maps/Debug/net10.0-android36.0/Microsoft.Maui.Maps.dll
  Controls.Core -> /home/vsts/work/1/s/artifacts/bin/Controls.Core/Debug/net10.0-android36.0/Microsoft.Maui.Controls.dll
  ##vso[build.updatebuildnumber]10.0.60-ci+azdo.13717716
  ##vso[build.updatebuildnumber]10.0.60-ci+azdo.13717716
  ##vso[build.updatebuildnumber]10.0.60-ci+azdo.13717716
  Controls.Xaml -> /home/vsts/work/1/s/artifacts/bin/Controls.Xaml/Debug/net10.0-android36.0/Microsoft.Maui.Controls.Xaml.dll
  Controls.Foldable -> /home/vsts/work/1/s/artifacts/bin/Controls.Foldable/Debug/net10.0-android36.0/Microsoft.Maui.Controls.Foldable.dll
  Controls.Maps -> /home/vsts/work/1/s/artifacts/bin/Controls.Maps/Debug/net10.0-android36.0/Microsoft.Maui.Controls.Maps.dll
  ##vso[build.updatebuildnumber]10.0.60-ci+azdo.13717716
  Microsoft.AspNetCore.Components.WebView.Maui -> /home/vsts/work/1/s/artifacts/bin/Microsoft.AspNetCore.Components.WebView.Maui/Debug/net10.0-android36.0/Microsoft.AspNetCore.Components.WebView.Maui.dll
  Controls.TestCases.HostApp -> /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.HostApp/Debug/net10.0-android/Controls.TestCases.HostApp.dll
  ##vso[build.updatebuildnumber]10.0.60-ci+azdo.13717716
  Graphics -> /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.HostApp/Debug/net10.0-android/Microsoft.Maui.Graphics.dll
  ##vso[build.updatebuildnumber]10.0.60-ci+azdo.13717716
  Essentials -> /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.HostApp/Debug/net10.0-android/Microsoft.Maui.Essentials.dll
  ##vso[build.updatebuildnumber]10.0.60-ci+azdo.13717716
  Core -> /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.HostApp/Debug/net10.0-android/Microsoft.Maui.dll
  Controls.BindingSourceGen -> /home/vsts/work/1/s/artifacts/bin/Controls.BindingSourceGen/Debug/netstandard2.0/Microsoft.Maui.Controls.BindingSourceGen.dll
  ##vso[build.updatebuildnumber]10.0.60-ci+azdo.13717716
  ##vso[build.updatebuildnumber]10.0.60-ci+azdo.13717716
  Maps -> /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.HostApp/Debug/net10.0-android/Microsoft.Maui.Maps.dll
  Controls.Core -> /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.HostApp/Debug/net10.0-android/Microsoft.Maui.Controls.dll
  ##vso[build.updatebuildnumber]10.0.60-ci+azdo.13717716
  ##vso[build.updatebuildnumber]10.0.60-ci+azdo.13717716
  ##vso[build.updatebuildnumber]10.0.60-ci+azdo.13717716
  ##vso[build.updatebuildnumber]10.0.60-ci+azdo.13717716
  Microsoft.AspNetCore.Components.WebView.Maui -> /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.HostApp/Debug/net10.0-android/Microsoft.AspNetCore.Components.WebView.Maui.dll
  Controls.Xaml -> /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.HostApp/Debug/net10.0-android/Microsoft.Maui.Controls.Xaml.dll
  Controls.Foldable -> /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.HostApp/Debug/net10.0-android/Microsoft.Maui.Controls.Foldable.dll
  Controls.Maps -> /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.HostApp/Debug/net10.0-android/Microsoft.Maui.Controls.Maps.dll

Build succeeded.
    0 Warning(s)
    0 Error(s)

Time Elapsed 00:06:35.95
Broadcasting: Intent { act=android.intent.action.CLOSE_SYSTEM_DIALOGS flg=0x400000 }
Broadcast completed: result=0
Broadcasting: Intent { act=android.intent.action.CLOSE_SYSTEM_DIALOGS flg=0x400000 }
Broadcast completed: result=0
  Determining projects to restore...
  All projects are up-to-date for restore.
  ##vso[build.updatebuildnumber]10.0.60-ci+azdo.13717716
  Graphics -> /home/vsts/work/1/s/artifacts/bin/Graphics/Debug/net10.0/Microsoft.Maui.Graphics.dll
  Controls.CustomAttributes -> /home/vsts/work/1/s/artifacts/bin/Controls.CustomAttributes/Debug/net10.0/Controls.CustomAttributes.dll
  ##vso[build.updatebuildnumber]10.0.60-ci+azdo.13717716
  Essentials -> /home/vsts/work/1/s/artifacts/bin/Essentials/Debug/net10.0/Microsoft.Maui.Essentials.dll
  ##vso[build.updatebuildnumber]10.0.60-ci+azdo.13717716
  Core -> /home/vsts/work/1/s/artifacts/bin/Core/Debug/net10.0/Microsoft.Maui.dll
  Controls.BindingSourceGen -> /home/vsts/work/1/s/artifacts/bin/Controls.BindingSourceGen/Debug/netstandard2.0/Microsoft.Maui.Controls.BindingSourceGen.dll
  ##vso[build.updatebuildnumber]10.0.60-ci+azdo.13717716
  Controls.Core -> /home/vsts/work/1/s/artifacts/bin/Controls.Core/Debug/net10.0/Microsoft.Maui.Controls.dll
  VisualTestUtils -> /home/vsts/work/1/s/artifacts/bin/VisualTestUtils/Debug/netstandard2.0/VisualTestUtils.dll
  UITest.Core -> /home/vsts/work/1/s/artifacts/bin/UITest.Core/Debug/net10.0/UITest.Core.dll
  VisualTestUtils.MagickNet -> /home/vsts/work/1/s/artifacts/bin/VisualTestUtils.MagickNet/Debug/netstandard2.0/VisualTestUtils.MagickNet.dll
  UITest.NUnit -> /home/vsts/work/1/s/artifacts/bin/UITest.NUnit/Debug/net10.0/UITest.NUnit.dll
  UITest.Appium -> /home/vsts/work/1/s/artifacts/bin/UITest.Appium/Debug/net10.0/UITest.Appium.dll
  UITest.Analyzers -> /home/vsts/work/1/s/artifacts/bin/UITest.Analyzers/Debug/netstandard2.0/UITest.Analyzers.dll
  Controls.TestCases.Android.Tests -> /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.Android.Tests/Debug/net10.0/Controls.TestCases.Android.Tests.dll
Test run for /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.Android.Tests/Debug/net10.0/Controls.TestCases.Android.Tests.dll (.NETCoreApp,Version=v10.0)
VSTest version 18.0.1 (x64)

Starting test execution, please wait...
A total of 1 test files matched the specified pattern.
/home/vsts/work/1/s/artifacts/bin/Controls.TestCases.Android.Tests/Debug/net10.0/Controls.TestCases.Android.Tests.dll
[xUnit.net 00:00:00.00] xUnit.net VSTest Adapter v2.8.2+699d445a1a (64-bit .NET 10.0.0)
[xUnit.net 00:00:00.11]   Discovering: Controls.TestCases.Android.Tests
[xUnit.net 00:00:00.46]   Discovered:  Controls.TestCases.Android.Tests
NUnit Adapter 4.5.0.0: Test execution started
Running selected tests in /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.Android.Tests/Debug/net10.0/Controls.TestCases.Android.Tests.dll
   NUnit3TestExecutor discovered 1 of 1 NUnit test cases using Current Discovery mode, Non-Explicit run
>>>>> 04/01/2026 22:47:39 FixtureSetup for Issue22197(Android)
>>>>> 04/01/2026 22:47:41 LineHeightWithHTMLLabelShouldWork Start
>>>>> 04/01/2026 22:47:45 LineHeightWithHTMLLabelShouldWork Stop
  Passed LineHeightWithHTMLLabelShouldWork [3 s]
NUnit Adapter 4.5.0.0: Test execution complete

Test Run Successful.
Total tests: 1
     Passed: 1
 Total time: 21.7490 Seconds

📁 Fix files reverted (3 files)
  • eng/pipelines/ci-copilot.yml
  • src/Controls/src/Core/Label/Label.Mapper.cs
  • src/Controls/src/Core/Label/Label.iOS.cs

New files (not reverted):

  • github-merge-flow-release-11.jsonc

@MauiBot
Copy link
Copy Markdown
Collaborator

MauiBot commented Apr 1, 2026

🤖 AI Summary

📊 Expand Full Review85c88f7 · Snapshots
🔍 Pre-Flight — Context & Validation

Issue: #22193 - LineHeight with HTML Label not working / #22197 - lineheight is broken
PR: #31202 - LineHeight and decorations for HTML Label - fix
Platforms Affected: Android, iOS
Files Changed: 2 implementation, 7 test (snapshots + XAML + UITest)

Key Findings

  • MapLineHeight, MapTextDecorations, and MapCharacterSpacing in Label.Mapper.cs were guarded by !IsPlainText(label) which returns true for HTML labels (TextType.Html), causing an early return and skipping the property mapping entirely
  • IsPlainText() returns false when either HasFormattedTextSpans OR TextType != TextType.Text — the original guard was overly broad, blocking HTML text labels from receiving these properties
  • The fix changes the guard to only HasFormattedTextSpans, allowing HTML labels to pass through the guard and have the properties applied
  • iOS Label.iOS.cs fix adds LineHeight, TextDecorations, and CharacterSpacing to the MapFormatting re-apply list when TextType == Html, so these properties also refresh when TextType changes
  • Test uses a screenshot comparison (UITest) with two labels side-by-side (HTML and plain text) to visually confirm parity
  • Open review comment: AutomationId="label" is too generic — should be more specific like "htmlTextTypeLabel"
  • MartyIX suggested more comprehensive lineHeight test cases (various LineHeight values) but PR author correctly noted this PR's scope is HTML label fix, not general lineHeight testing

Fix Candidates

# Source Approach Test Result Files Changed Notes
PR PR #31202 Change !IsPlainText(label)label.HasFormattedTextSpans in 3 mapper methods; add LineHeight/TextDecorations/CharacterSpacing to iOS MapFormatting ✅ PASSED (Gate) Label.Mapper.cs, Label.iOS.cs Clean minimal fix

🔧 Fix — Analysis & Comparison

Fix Candidates

# Source Approach Test Result Files Changed Notes
1 try-fix (claude-opus-4.6) label.IsSet() guard — apply LineHeight/TextDecorations/CharacterSpacing to HTML labels only when property explicitly set, following MapFont/MapTextColor pattern ✅ PASS Label.Mapper.cs, Label.iOS.cs Conservative; extra complexity without clear benefit
2 try-fix (claude-sonnet-4.6) Non-default value check — label.LineHeight != -1.0, label.TextDecorations != None, label.CharacterSpacing != 0.0 ✅ PASS Label.Mapper.cs, Label.iOS.cs Hardcoded magic default values; more fragile
3 try-fix (gpt-5.3-codex) TextType-transition approach — trigger property refreshes from MapTextType when TextType==Html ✅ PASS Label.Mapper.cs Single-file fix; misses case where properties set before TextType
4 try-fix (gpt-5.4, gemini unavailable) Rename IsPlainText()ShouldSkipFormattingForSpans() with only HasFormattedTextSpans check ✅ PASS Label.Mapper.cs, Label.iOS.cs Functionally identical to PR's fix; just a rename
PR PR #31202 Change !IsPlainText(label)label.HasFormattedTextSpans in 3 mapper methods; add LineHeight/TextDecorations/CharacterSpacing to iOS MapFormatting ✅ PASSED (Gate) Label.Mapper.cs, Label.iOS.cs Simplest direct fix; minimal changes

Cross-Pollination

Model Round New Ideas? Details
claude-opus-4.6 1 No NO NEW IDEAS
claude-sonnet-4.6 1 No NO NEW IDEAS
gpt-5.3-codex 1 Yes → No (R2) Suggested HTML pipeline approach; withdrawn after analysis showed conflict with HTML/CSS
gpt-5.4 1 Yes → No (R2) Same HTML pipeline suggestion; withdrawn after analysis

Exhausted: Yes
Selected Fix: PR's fix — Simplest and most direct. The !IsPlainText(label)label.HasFormattedTextSpans change correctly identifies that the only reason to skip these properties is FormattedText spans, not HTML mode. Attempts 1-2 add unnecessary complexity. Attempt 3 is incomplete (misses initial render). Attempt 4 is functionally identical but adds a rename. PR fix is optimal.


📋 Report — Final Recommendation

✅ Final Recommendation: APPROVE

Phase Status

Phase Status Notes
Pre-Flight ✅ COMPLETE 2 issues (#22193, #22197), Android+iOS platforms
Gate ✅ PASSED android — LineHeightWithHTMLLabelShouldWork passes with fix
Try-Fix ✅ COMPLETE 4 attempts, all 4 passing; cross-pollination exhausted in 2 rounds
Report ✅ COMPLETE

Summary

PR #31202 fixes LineHeight, TextDecorations, and CharacterSpacing not being applied to HTML labels (TextType="Html") on Android and iOS. The fix is minimal (6 lines changed across 2 implementation files) and surgically correct. Four independent fix attempts all passed, and cross-pollination across all models confirmed no better alternative exists. The PR's approach is the simplest of all candidates.

Root Cause

MapLineHeight, MapTextDecorations, and MapCharacterSpacing in Label.Mapper.cs were guarded by !IsPlainText(label). IsPlainText() returns false when either HasFormattedTextSpans is true OR TextType != TextType.Text — meaning HTML labels triggered the early return and never had these properties applied. The guard was semantically wrong: the intent was to skip span-based FormattedText, but HTML text got caught in the same exclusion. On iOS, MapFormatting also didn't refresh these properties when TextType changes to Html.

Fix Quality

Excellent. The change is:

  • Correct: Replaces !IsPlainText(label) with label.HasFormattedTextSpans, which is exactly the right predicate — only skip when FormattedText spans are present, not for HTML
  • Minimal: 3 one-line changes in the mapper, 3 added lines in the iOS formatter
  • Consistent: IsPlainText() itself is still used in Label.iOS.cs (MapFormatting), so it remains without breaking existing callers
  • Well-tested: UITest screenshot comparison for both HTML and plain text labels side-by-side

Minor issues identified:

  1. AutomationId="label" is too generic (noted in review comment from copilot-pull-request-reviewer) — consider "htmlTextTypeLabel" for uniqueness
  2. Missing newline at end of 3 new test files (Issue22197.xaml, Issue22197.xaml.cs, Issue22197.cs) — cosmetic only
  3. Snapshots added for Android, Mac, Windows, and iOS-26 but not other iOS configurations — acceptable for screenshot tests

Try-Fix comparison: 4 independent attempts all passed but each was more complex than the PR's approach:

  • Attempt 1 adds IsSet() guards (over-engineering)
  • Attempt 2 hardcodes default values (fragile)
  • Attempt 3 triggers from MapTextType (incomplete — misses initial render order)
  • Attempt 4 is functionally identical with an unnecessary rename

The PR's simple 2-character guard replacement (!IsPlainTextHasFormattedTextSpans) is the optimal solution.


@MauiBot MauiBot added s/agent-approved AI agent recommends approval - PR fix is correct and optimal s/agent-fix-pr-picked AI could not beat the PR fix - PR is the best among all candidates s/agent-reviewed PR was reviewed by AI agent workflow (full 4-phase review) labels Apr 1, 2026
@kubaflo kubaflo changed the base branch from main to inflight/current April 2, 2026 09:41
@kubaflo kubaflo merged commit eda0b73 into dotnet:inflight/current Apr 2, 2026
35 of 36 checks passed
PureWeen pushed a commit that referenced this pull request Apr 2, 2026
- Replace non-existent PR numbers (#34000, #33500, #33000, #34100)
  with real merged PRs (#34024, #34727, #31202, #28713, #34723)
- Add "in dotnet/maui" to all prompts to prevent agent asking for repo
- All PRs verified as real merged PRs with actual code changes

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

area-controls-label Label, Span platform/android platform/ios s/agent-approved AI agent recommends approval - PR fix is correct and optimal s/agent-fix-pr-picked AI could not beat the PR fix - PR is the best among all candidates s/agent-reviewed PR was reviewed by AI agent workflow (full 4-phase review)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

lineheight is broken LineHeight with HTML Label not working

6 participants