Skip to content

Fix CollectionView group header not stretching to full width on Windows#34710

Open
rmarinho wants to merge 2 commits intomainfrom
fix/collectionview-group-header-width-23038
Open

Fix CollectionView group header not stretching to full width on Windows#34710
rmarinho wants to merge 2 commits intomainfrom
fix/collectionview-group-header-width-23038

Conversation

@rmarinho
Copy link
Copy Markdown
Member

Description of Change

The ListViewHeaderItem and GridViewHeaderItem styles in ItemsViewStyles.xaml had HorizontalContentAlignment set to Left and Padding of 12,8,12,0 which prevented group headers from stretching to match item width.

Changed to HorizontalContentAlignment=Stretch with zero padding to match how regular item containers are configured. Preserved the 4px bottom margin for visual separation between headers and items.

As this is a breaking change i think we might want to net11.0 ?

Issues Fixed

Fixes #23038
Fixes #16824

The ListViewHeaderItem and GridViewHeaderItem styles in ItemsViewStyles.xaml
had HorizontalContentAlignment set to Left and Padding of 12,8,12,0 which
prevented group headers from stretching to match item width.

Changed to HorizontalContentAlignment=Stretch with zero padding to match
how regular item containers are configured. Preserved the 4px bottom margin
for visual separation between headers and items.

Fixes #23038
Fixes #16824

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings March 28, 2026 01:46
@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Mar 28, 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 -- 34710

Or

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

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

Fixes a Windows-specific CollectionView grouped header layout issue by updating the default WinUI header container styles to allow header content to stretch full width, and adds a new UI regression test (HostApp page + Appium/NUnit test) for issue #23038.

Changes:

  • Updated WinUI ListViewHeaderItem/GridViewHeaderItem styles to use HorizontalContentAlignment="Stretch" and remove default padding.
  • Added a HostApp reproduction page for a grouped CollectionView with header/footer templates.
  • Added an Appium-based UI test validating group header width matches the item/footer width.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 5 comments.

File Description
src/Controls/src/Core/Platform/Windows/CollectionView/ItemsViewStyles.xaml Adjusts Windows header container defaults to allow group header templates to stretch full width.
src/Controls/tests/TestCases.HostApp/Issues/Issue23038.cs Adds a grouped CollectionView scenario with header/footer templates and AutomationIds for testing.
src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue23038.cs Adds an Appium UI test asserting header width stretches to match the CollectionView/item/footer width.

Comment on lines +212 to +215
<Setter Property="Padding" Value="0,0,0,0" />
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
<Setter Property="VerticalContentAlignment" Value="Top" />
<Setter Property="MinHeight" Value="{ThemeResource ListViewHeaderItemMinHeight}" />
<Setter Property="MinHeight" Value="0" />
Copy link

Copilot AI Mar 28, 2026

Choose a reason for hiding this comment

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

MinHeight for ListViewHeaderItem was changed from the platform theme resource to 0. This is unrelated to the width/stretching fix and risks regressing header sizing/touch target consistency. Consider keeping MinHeight as {ThemeResource ListViewHeaderItemMinHeight} (or justify why it must be 0).

Copilot uses AI. Check for mistakes.
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
<Setter Property="VerticalContentAlignment" Value="Top" />
<Setter Property="MinHeight" Value="{ThemeResource GridViewHeaderItemMinHeight}" />
<Setter Property="MinHeight" Value="0" />
Copy link

Copilot AI Mar 28, 2026

Choose a reason for hiding this comment

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

MinHeight for GridViewHeaderItem was changed from the platform theme resource to 0. Unless this is required for the stretching fix, it likely causes an unintended UI regression (header height/touch target). Consider reverting to {ThemeResource GridViewHeaderItemMinHeight}.

Suggested change
<Setter Property="MinHeight" Value="0" />
<Setter Property="MinHeight" Value="{ThemeResource GridViewHeaderItemMinHeight}" />

Copilot uses AI. Check for mistakes.
Comment on lines +33 to +35
label.SetBinding(Label.TextProperty, "Name");
label.SetBinding(Label.AutomationIdProperty, "Name", stringFormat: "Header_{0}");
return label;
Copy link

Copilot AI Mar 28, 2026

Choose a reason for hiding this comment

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

The group header AutomationId is being generated from the bound Name value ("Team A"), which produces IDs containing spaces/underscores (e.g., Header_Team A). For UI test stability, prefer deterministic PascalCase AutomationIds without spaces (e.g., derive from a sanitized key or set explicit IDs).

Copilot generated this review using guidance from repository custom instructions.
Comment on lines +44 to +46
label.SetBinding(Label.TextProperty, "Count", stringFormat: "Total: {0}");
label.SetBinding(Label.AutomationIdProperty, "Name", stringFormat: "Footer_{0}");
return label;
Copy link

Copilot AI Mar 28, 2026

Choose a reason for hiding this comment

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

The group footer AutomationId is being generated from the bound Name value ("Team A"), resulting in IDs with spaces (e.g., Footer_Team A). Consider using a sanitized key / explicit AutomationIds that follow the repo's UI test conventions (PascalCase, no spaces) to keep Appium selectors reliable.

Copilot generated this review using guidance from repository custom instructions.
Comment on lines +22 to +26
// Get the group header bounds
var headerRect = App.WaitForElement("Header_Team A").GetRect();

// Get the group footer bounds (footers are regular list items, they already stretch)
var footerRect = App.WaitForElement("Footer_Team A").GetRect();
Copy link

Copilot AI Mar 28, 2026

Choose a reason for hiding this comment

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

This test locates elements by AutomationIds containing spaces (e.g., "Header_Team A", "Footer_Team A"). These tend to be brittle across platforms/drivers; it would be more reliable to use sanitized/deterministic PascalCase AutomationIds (and update the HostApp page accordingly).

Copilot generated this review using guidance from repository custom instructions.
@github-actions
Copy link
Copy Markdown
Contributor

🧪 PR Test Evaluation

Overall Verdict: ⚠️ Tests need improvement

The UI test covers the primary fix scenario (list-based grouped CollectionView header stretching) but misses coverage for the grid-based layout path that the fix also modifies.

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

📊 Expand Full Evaluation

PR Test Evaluation Report

PR: #34710 — Fix CollectionView group header not stretching to full width on Windows
Test files evaluated: 2 (1 NUnit test + 1 HostApp page)
Fix files: 1


Overall Verdict

⚠️ Tests need improvement

The test validates the primary fix (header stretching to full width in list-based CollectionView), but the fix also modifies GridViewHeaderItem styles (for grid-based layouts), and that code path is not covered by any test.


1. Fix Coverage — ✅

The fix changes ListViewHeaderItem and GridViewHeaderItem styles in ItemsViewStyles.xaml:

  • Padding: 12,8,12,00,0,0,0
  • HorizontalContentAlignment: LeftStretch
  • MinHeight: {ThemeResource ...}0

The test creates a grouped CollectionView, measures the rendered width of a group header, and compares it against the group footer width (which already stretched correctly). This directly exercises the HorizontalContentAlignment=Stretch fix for ListViewHeaderItem. The test would fail if the fix were reverted.

Minor gap: the Padding and MinHeight changes are not explicitly verified, but they are secondary effects supporting the primary width fix.


2. Edge Cases & Gaps — ⚠️

Covered:

  • Default list-based grouped CollectionView (ItemsLayout defaulting to LinearItemsLayout)
  • Multiple groups present (Team A, Team B) — only Team A measured, but reasonable
  • Header vs footer width comparison provides a good behavioral baseline

Missing:

  • Grid-based layout: The fix also changes GridViewHeaderItem (used when CollectionView.ItemsLayout is a GridItemsLayout). No test exercises this code path. A test with ItemsLayout = new GridItemsLayout(2, ItemsLayoutOrientation.Vertical) would be needed to cover this.
  • Direct width assertion: The test compares headerRect.Width ≈ footerRect.Width rather than headerRect.Width ≈ collectionViewRect.Width. If both footer and header were equally narrow (e.g., wrong size for a different reason), the test would still pass. The 20px footer-vs-CV tolerance provides a reasonable baseline check, but a tighter direct assertion would be stronger.

3. Test Type Appropriateness — ✅

Current: UI Test (Appium)
Recommendation: Same — UI test is appropriate here

The fix is in Windows XAML styles, and the bug is a visual layout issue requiring actual WinUI rendering to measure element widths. A unit test or XAML test cannot verify rendered pixel widths. A device test could theoretically measure layout rects, but Appium-based UI measurement via GetRect() is the established pattern for this type of layout verification in MAUI.


4. Convention Compliance — ✅

No convention issues detected by the automated script. Verified manually:

  • ✅ File naming: Issue23038.cs
  • [Issue(IssueTracker.Github, 23038, "...", PlatformAffected.UWP)] on HostApp page
  • [Category(UITestCategories.CollectionView)] — exactly one category attribute
  • ✅ Inherits from _IssuesUITest
  • WaitForElement before all GetRect() calls
  • ✅ No Task.Delay / Thread.Sleep
  • ✅ No inline #if ANDROID/#if IOS directives
  • ✅ No obsolete APIs (Application.MainPage, Frame, etc.)

5. Flakiness Risk — ⚠️ Medium

The test relies on GetRect() width measurements with fixed tolerances:

  • footerRect.Width == collectionViewRect.Width ± 20px — generous tolerance, low risk
  • headerRect.Width == footerRect.Width ± 2px — tight tolerance, slightly higher risk if there's sub-pixel rendering variance on Windows

There is no VerifyScreenshot() so cursor blink is not a concern. The tight 2px tolerance between header and footer could be fragile if WinUI renders these with any fractional-pixel difference. Consider increasing the tolerance slightly or using Within(5) as a safer margin.


6. Duplicate Coverage — ✅ No duplicates

The script matched tests containing "Platform" in the keyword but none are related to this issue. No existing test covers grouped CollectionView group header width on Windows.


7. Platform Scope — ✅

The fix is Windows-only (Platform/Windows/CollectionView/ItemsViewStyles.xaml), and the HostApp page correctly declares PlatformAffected.UWP. The test will only run on Windows. This is appropriate.


8. Assertion Quality — ⚠️

The assertions are reasonably specific:

  • ✅ Compares actual rendered widths, not just presence/null checks
  • ✅ Two-step comparison (footer proves baseline, header must match footer) is logical
  • ⚠️ Comparing headerRect.Width ≈ footerRect.Width is indirect — if both were wrong together, the test would still pass
  • ⚠️ 2px tolerance for the header-footer comparison may be too tight for CI stability on different Windows DPI settings

A stronger assertion would be: Assert.That(headerRect.Width, Is.EqualTo(collectionViewRect.Width).Within(20)) directly, rather than routing through the footer as a proxy.


9. Fix-Test Alignment — ⚠️

Fix touches: ListViewHeaderItem style AND GridViewHeaderItem style
Test exercises: Only list-based CollectionView (uses ListViewHeaderItem)

The GridViewHeaderItem code path is untested. This is the main alignment gap.


Recommendations

  1. Add a grid-layout test — Add a second test method (or a second HostApp page) with a grid-based grouped CollectionView using ItemsLayout = new GridItemsLayout(2, ItemsLayoutOrientation.Vertical) to verify the GridViewHeaderItem fix works as well.

  2. Strengthen the width assertion — Consider asserting headerRect.Width ≈ collectionViewRect.Width directly (within 20px) rather than only comparing header to footer. This makes the test self-sufficient and doesn't rely on the footer being correct as a proxy.

  3. Slightly relax the header-footer tolerance — The current Within(2) for the header-footer comparison is very tight. Consider Within(5) to reduce flakiness risk from DPI-related rendering differences across Windows CI machines.

Warning

⚠️ Firewall blocked 1 domain

The following domain was blocked by the firewall during workflow execution:

  • dc.services.visualstudio.com

To allow these domains, add them to the network.allowed list in your workflow frontmatter:

network:
  allowed:
    - defaults
    - "dc.services.visualstudio.com"

See Network Configuration for more information.

Note

🔒 Integrity filtering filtered 1 item

Integrity filtering activated and filtered the following item during workflow execution.
This happens when a tool call accesses a resource that does not meet the required integrity or secrecy level of the workflow.

🧪 Test evaluation by Evaluate PR Tests

@MauiBot
Copy link
Copy Markdown
Collaborator

MauiBot commented Mar 30, 2026

🚦 Gate - Test Before and After Fix

📊 Expand Full Gatecd02449 · Fix CollectionView group header not stretching to full width on Windows

Gate Result: ✅ PASSED

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

Test Without Fix (expect FAIL) With Fix (expect PASS)
🖥️ Issue23038 Issue23038 ✅ FAIL — 578s ✅ PASS — 477s
🔴 Without fix — 🖥️ Issue23038: FAIL ✅ · 578s
  Determining projects to restore...
  Restored D:\a\1\s\src\Controls\src\Core\Controls.Core.csproj (in 47.57 sec).
  Restored D:\a\1\s\src\Controls\Maps\src\Controls.Maps.csproj (in 48.76 sec).
  Restored D:\a\1\s\src\Controls\Foldable\src\Controls.Foldable.csproj (in 1.25 sec).
  Restored D:\a\1\s\src\BlazorWebView\src\Maui\Microsoft.AspNetCore.Components.WebView.Maui.csproj (in 3.51 sec).
  Restored D:\a\1\s\src\Graphics\src\Graphics.Win2D\Graphics.Win2D.csproj (in 24 ms).
  Restored D:\a\1\s\src\Essentials\src\Essentials.csproj (in 46 ms).
  Restored D:\a\1\s\src\Core\src\Core.csproj (in 70 ms).
  Restored D:\a\1\s\src\Core\maps\src\Maps.csproj (in 24 ms).
  Restored D:\a\1\s\src\Graphics\src\Graphics\Graphics.csproj (in 3.89 sec).
  Restored D:\a\1\s\src\Controls\src\Xaml\Controls.Xaml.csproj (in 23 ms).
  Restored D:\a\1\s\src\Controls\tests\TestCases.HostApp\Controls.TestCases.HostApp.csproj (in 702 ms).
  3 of 14 projects are up-to-date for restore.
  ##vso[build.updatebuildnumber]10.0.60-ci+azdo.13692688
  Graphics -> D:\a\1\s\artifacts\bin\Graphics\Debug\net10.0-windows10.0.19041.0\Microsoft.Maui.Graphics.dll
  ##vso[build.updatebuildnumber]10.0.60-ci+azdo.13692688
  Graphics.Win2D -> D:\a\1\s\artifacts\bin\Graphics.Win2D\Debug\net10.0-windows10.0.19041.0\Microsoft.Maui.Graphics.Win2D.WinUI.Desktop.dll
  ##vso[build.updatebuildnumber]10.0.60-ci+azdo.13692688
  Essentials -> D:\a\1\s\artifacts\bin\Essentials\Debug\net10.0-windows10.0.19041.0\Microsoft.Maui.Essentials.dll
  ##vso[build.updatebuildnumber]10.0.60-ci+azdo.13692688
  Core -> D:\a\1\s\artifacts\bin\Core\Debug\net10.0-windows10.0.19041.0\Microsoft.Maui.dll
  Controls.BindingSourceGen -> D:\a\1\s\artifacts\bin\Controls.BindingSourceGen\Debug\netstandard2.0\Microsoft.Maui.Controls.BindingSourceGen.dll
  ##vso[build.updatebuildnumber]10.0.60-ci+azdo.13692688
  ##vso[build.updatebuildnumber]10.0.60-ci+azdo.13692688
  Maps -> D:\a\1\s\artifacts\bin\Maps\Debug\net10.0-windows10.0.19041.0\Microsoft.Maui.Maps.dll
  Controls.Core -> D:\a\1\s\artifacts\bin\Controls.Core\Debug\net10.0-windows10.0.19041.0\Microsoft.Maui.Controls.dll
  ##vso[build.updatebuildnumber]10.0.60-ci+azdo.13692688
  ##vso[build.updatebuildnumber]10.0.60-ci+azdo.13692688
  ##vso[build.updatebuildnumber]10.0.60-ci+azdo.13692688
  ##vso[build.updatebuildnumber]10.0.60-ci+azdo.13692688
  Microsoft.AspNetCore.Components.WebView.Maui -> D:\a\1\s\artifacts\bin\Microsoft.AspNetCore.Components.WebView.Maui\Debug\net10.0-windows10.0.19041.0\Microsoft.AspNetCore.Components.WebView.Maui.dll
  Controls.Maps -> D:\a\1\s\artifacts\bin\Controls.Maps\Debug\net10.0-windows10.0.19041.0\Microsoft.Maui.Controls.Maps.dll
  Controls.Xaml -> D:\a\1\s\artifacts\bin\Controls.Xaml\Debug\net10.0-windows10.0.19041.0\Microsoft.Maui.Controls.Xaml.dll
  Controls.Foldable -> D:\a\1\s\artifacts\bin\Controls.Foldable\Debug\net10.0-windows10.0.19041.0\Microsoft.Maui.Controls.Foldable.dll
  Controls.TestCases.HostApp -> D:\a\1\s\artifacts\bin\Controls.TestCases.HostApp\Debug\net10.0-windows10.0.19041.0\win-x64\Controls.TestCases.HostApp.dll

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

Time Elapsed 00:05:59.73
  Determining projects to restore...
  Restored D:\a\1\s\src\Controls\tests\CustomAttributes\Controls.CustomAttributes.csproj (in 926 ms).
  Restored D:\a\1\s\src\TestUtils\src\VisualTestUtils\VisualTestUtils.csproj (in 4 ms).
  Restored D:\a\1\s\src\TestUtils\src\VisualTestUtils.MagickNet\VisualTestUtils.MagickNet.csproj (in 4.35 sec).
  Restored D:\a\1\s\src\Controls\tests\TestCases.WinUI.Tests\Controls.TestCases.WinUI.Tests.csproj (in 9.11 sec).
  Restored D:\a\1\s\src\TestUtils\src\UITest.Core\UITest.Core.csproj (in 2 ms).
  Restored D:\a\1\s\src\TestUtils\src\UITest.Appium\UITest.Appium.csproj (in 7 ms).
  Restored D:\a\1\s\src\TestUtils\src\UITest.NUnit\UITest.NUnit.csproj (in 2.68 sec).
  Restored D:\a\1\s\src\TestUtils\src\UITest.Analyzers\UITest.Analyzers.csproj (in 5.95 sec).
  7 of 15 projects are up-to-date for restore.
  ##vso[build.updatebuildnumber]10.0.60-ci+azdo.13692688
  Graphics -> D:\a\1\s\artifacts\bin\Graphics\Debug\net10.0\Microsoft.Maui.Graphics.dll
  ##vso[build.updatebuildnumber]10.0.60-ci+azdo.13692688
  Essentials -> D:\a\1\s\artifacts\bin\Essentials\Debug\net10.0\Microsoft.Maui.Essentials.dll
  Controls.CustomAttributes -> D:\a\1\s\artifacts\bin\Controls.CustomAttributes\Debug\net10.0\Controls.CustomAttributes.dll
  ##vso[build.updatebuildnumber]10.0.60-ci+azdo.13692688
  Core -> D:\a\1\s\artifacts\bin\Core\Debug\net10.0\Microsoft.Maui.dll
  Controls.Core.Design -> D:\a\1\s\artifacts\bin\Controls.Core.Design\Debug\net472\Microsoft.Maui.Controls.DesignTools.dll
  Controls.BindingSourceGen -> D:\a\1\s\artifacts\bin\Controls.BindingSourceGen\Debug\netstandard2.0\Microsoft.Maui.Controls.BindingSourceGen.dll
  ##vso[build.updatebuildnumber]10.0.60-ci+azdo.13692688
  Controls.Core -> D:\a\1\s\artifacts\bin\Controls.Core\Debug\net10.0\Microsoft.Maui.Controls.dll
  UITest.Core -> D:\a\1\s\artifacts\bin\UITest.Core\Debug\net10.0\UITest.Core.dll
  UITest.Appium -> D:\a\1\s\artifacts\bin\UITest.Appium\Debug\net10.0\UITest.Appium.dll
  UITest.NUnit -> D:\a\1\s\artifacts\bin\UITest.NUnit\Debug\net10.0\UITest.NUnit.dll
  VisualTestUtils -> D:\a\1\s\artifacts\bin\VisualTestUtils\Debug\netstandard2.0\VisualTestUtils.dll
  VisualTestUtils.MagickNet -> D:\a\1\s\artifacts\bin\VisualTestUtils.MagickNet\Debug\netstandard2.0\VisualTestUtils.MagickNet.dll
  UITest.Analyzers -> D:\a\1\s\artifacts\bin\UITest.Analyzers\Debug\netstandard2.0\UITest.Analyzers.dll
  Controls.TestCases.WinUI.Tests -> D:\a\1\s\artifacts\bin\Controls.TestCases.WinUI.Tests\Debug\net10.0\Controls.TestCases.WinUI.Tests.dll
Test run for D:\a\1\s\artifacts\bin\Controls.TestCases.WinUI.Tests\Debug\net10.0\Controls.TestCases.WinUI.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.
D:\a\1\s\artifacts\bin\Controls.TestCases.WinUI.Tests\Debug\net10.0\Controls.TestCases.WinUI.Tests.dll
NUnit Adapter 4.5.0.0: Test execution started
Running selected tests in D:\a\1\s\artifacts\bin\Controls.TestCases.WinUI.Tests\Debug\net10.0\Controls.TestCases.WinUI.Tests.dll
   NUnit3TestExecutor discovered 1 of 1 NUnit test cases using Current Discovery mode, Non-Explicit run
>>>>> 3/30/2026 10:30:31 PM FixtureSetup for Issue23038(Windows)
>>>>> 3/30/2026 10:30:40 PM GroupHeaderShouldStretchToFullWidth Start
>>>>> 3/30/2026 10:30:43 PM GroupHeaderShouldStretchToFullWidth Stop
>>>>> 3/30/2026 10:30:43 PM Log types: 
  Failed GroupHeaderShouldStretchToFullWidth [3 s]
  Error Message:
     Group header width should match group footer width
Assert.That(headerRect.Width, Is.EqualTo(footerRect.Width).Within(2))
  Expected: 1008 +/- 2
  But was:  984
  Off by:   24.0d

  Stack Trace:
     at Microsoft.Maui.TestCases.Tests.Issues.Issue23038.GroupHeaderShouldStretchToFullWidth() in /_/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue23038.cs:line 33

1)    at Microsoft.Maui.TestCases.Tests.Issues.Issue23038.GroupHeaderShouldStretchToFullWidth() in /_/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue23038.cs:line 33


NUnit Adapter 4.5.0.0: Test execution complete
[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.WinUI.Tests
[xUnit.net 00:00:00.32]   Discovered:  Controls.TestCases.WinUI.Tests

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

🟢 With fix — 🖥️ Issue23038: PASS ✅ · 477s
  Determining projects to restore...
  All projects are up-to-date for restore.
  ##vso[build.updatebuildnumber]10.0.60-ci+azdo.13692688
  Graphics -> D:\a\1\s\artifacts\bin\Graphics\Debug\net10.0-windows10.0.19041.0\Microsoft.Maui.Graphics.dll
  ##vso[build.updatebuildnumber]10.0.60-ci+azdo.13692688
  ##vso[build.updatebuildnumber]10.0.60-ci+azdo.13692688
  Essentials -> D:\a\1\s\artifacts\bin\Essentials\Debug\net10.0-windows10.0.19041.0\Microsoft.Maui.Essentials.dll
  Graphics.Win2D -> D:\a\1\s\artifacts\bin\Graphics.Win2D\Debug\net10.0-windows10.0.19041.0\Microsoft.Maui.Graphics.Win2D.WinUI.Desktop.dll
  ##vso[build.updatebuildnumber]10.0.60-ci+azdo.13692688
  Core -> D:\a\1\s\artifacts\bin\Core\Debug\net10.0-windows10.0.19041.0\Microsoft.Maui.dll
  ##vso[build.updatebuildnumber]10.0.60-ci+azdo.13692688
  Maps -> D:\a\1\s\artifacts\bin\Maps\Debug\net10.0-windows10.0.19041.0\Microsoft.Maui.Maps.dll
  Controls.BindingSourceGen -> D:\a\1\s\artifacts\bin\Controls.BindingSourceGen\Debug\netstandard2.0\Microsoft.Maui.Controls.BindingSourceGen.dll
  ##vso[build.updatebuildnumber]10.0.60-ci+azdo.13692688
  Controls.Core -> D:\a\1\s\artifacts\bin\Controls.Core\Debug\net10.0-windows10.0.19041.0\Microsoft.Maui.Controls.dll
  ##vso[build.updatebuildnumber]10.0.60-ci+azdo.13692688
  ##vso[build.updatebuildnumber]10.0.60-ci+azdo.13692688
  ##vso[build.updatebuildnumber]10.0.60-ci+azdo.13692688
  Controls.Xaml -> D:\a\1\s\artifacts\bin\Controls.Xaml\Debug\net10.0-windows10.0.19041.0\Microsoft.Maui.Controls.Xaml.dll
  ##vso[build.updatebuildnumber]10.0.60-ci+azdo.13692688
  Microsoft.AspNetCore.Components.WebView.Maui -> D:\a\1\s\artifacts\bin\Microsoft.AspNetCore.Components.WebView.Maui\Debug\net10.0-windows10.0.19041.0\Microsoft.AspNetCore.Components.WebView.Maui.dll
  Controls.Foldable -> D:\a\1\s\artifacts\bin\Controls.Foldable\Debug\net10.0-windows10.0.19041.0\Microsoft.Maui.Controls.Foldable.dll
  Controls.Maps -> D:\a\1\s\artifacts\bin\Controls.Maps\Debug\net10.0-windows10.0.19041.0\Microsoft.Maui.Controls.Maps.dll
  Controls.TestCases.HostApp -> D:\a\1\s\artifacts\bin\Controls.TestCases.HostApp\Debug\net10.0-windows10.0.19041.0\win-x64\Controls.TestCases.HostApp.dll

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

Time Elapsed 00:05:52.69
  Determining projects to restore...
  All projects are up-to-date for restore.
  ##vso[build.updatebuildnumber]10.0.60-ci+azdo.13692688
  Graphics -> D:\a\1\s\artifacts\bin\Graphics\Debug\net10.0\Microsoft.Maui.Graphics.dll
  Controls.CustomAttributes -> D:\a\1\s\artifacts\bin\Controls.CustomAttributes\Debug\net10.0\Controls.CustomAttributes.dll
  ##vso[build.updatebuildnumber]10.0.60-ci+azdo.13692688
  Essentials -> D:\a\1\s\artifacts\bin\Essentials\Debug\net10.0\Microsoft.Maui.Essentials.dll
  ##vso[build.updatebuildnumber]10.0.60-ci+azdo.13692688
  Core -> D:\a\1\s\artifacts\bin\Core\Debug\net10.0\Microsoft.Maui.dll
  Controls.Core.Design -> D:\a\1\s\artifacts\bin\Controls.Core.Design\Debug\net472\Microsoft.Maui.Controls.DesignTools.dll
  Controls.BindingSourceGen -> D:\a\1\s\artifacts\bin\Controls.BindingSourceGen\Debug\netstandard2.0\Microsoft.Maui.Controls.BindingSourceGen.dll
  ##vso[build.updatebuildnumber]10.0.60-ci+azdo.13692688
  Controls.Core -> D:\a\1\s\artifacts\bin\Controls.Core\Debug\net10.0\Microsoft.Maui.Controls.dll
  UITest.Core -> D:\a\1\s\artifacts\bin\UITest.Core\Debug\net10.0\UITest.Core.dll
  VisualTestUtils -> D:\a\1\s\artifacts\bin\VisualTestUtils\Debug\netstandard2.0\VisualTestUtils.dll
  UITest.Appium -> D:\a\1\s\artifacts\bin\UITest.Appium\Debug\net10.0\UITest.Appium.dll
  UITest.NUnit -> D:\a\1\s\artifacts\bin\UITest.NUnit\Debug\net10.0\UITest.NUnit.dll
  VisualTestUtils.MagickNet -> D:\a\1\s\artifacts\bin\VisualTestUtils.MagickNet\Debug\netstandard2.0\VisualTestUtils.MagickNet.dll
  UITest.Analyzers -> D:\a\1\s\artifacts\bin\UITest.Analyzers\Debug\netstandard2.0\UITest.Analyzers.dll
  Controls.TestCases.WinUI.Tests -> D:\a\1\s\artifacts\bin\Controls.TestCases.WinUI.Tests\Debug\net10.0\Controls.TestCases.WinUI.Tests.dll
Test run for D:\a\1\s\artifacts\bin\Controls.TestCases.WinUI.Tests\Debug\net10.0\Controls.TestCases.WinUI.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.
D:\a\1\s\artifacts\bin\Controls.TestCases.WinUI.Tests\Debug\net10.0\Controls.TestCases.WinUI.Tests.dll
NUnit Adapter 4.5.0.0: Test execution started
Running selected tests in D:\a\1\s\artifacts\bin\Controls.TestCases.WinUI.Tests\Debug\net10.0\Controls.TestCases.WinUI.Tests.dll
   NUnit3TestExecutor discovered 1 of 1 NUnit test cases using Current Discovery mode, Non-Explicit run
>>>>> 3/30/2026 10:38:30 PM FixtureSetup for Issue23038(Windows)
>>>>> 3/30/2026 10:38:39 PM GroupHeaderShouldStretchToFullWidth Start
>>>>> 3/30/2026 10:38:42 PM GroupHeaderShouldStretchToFullWidth Stop
  Passed GroupHeaderShouldStretchToFullWidth [2 s]
NUnit Adapter 4.5.0.0: Test execution complete
[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.14]   Discovering: Controls.TestCases.WinUI.Tests
[xUnit.net 00:00:00.34]   Discovered:  Controls.TestCases.WinUI.Tests

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

📁 Fix files reverted (2 files)
  • eng/pipelines/ci-copilot.yml
  • src/Controls/src/Core/Platform/Windows/CollectionView/ItemsViewStyles.xaml

@MauiBot
Copy link
Copy Markdown
Collaborator

MauiBot commented Mar 30, 2026

🤖 AI Summary

📊 Expand Full Reviewcd02449 · Fix CollectionView group header not stretching to full width on Windows
🔍 Pre-Flight — Context & Validation

Issue: #23038 - [Windows] GroupHeaderTemplate width smaller than ItemTemplate
Issue (also fixed): #16824 - Group in a CollectionView on windows does not span horizontally
PR: #34710 - Fix CollectionView group header not stretching to full width on Windows
Author: rmarinho
Platforms Affected: Windows only
Files Changed: 1 implementation, 2 test

Key Findings

  • The root cause is in src/Controls/src/Core/Platform/Windows/CollectionView/ItemsViewStyles.xamlListViewHeaderItem and GridViewHeaderItem styles had HorizontalContentAlignment=Left and Padding=12,8,12,0, forcing headers to be narrower than items.
  • Fix changes both styles to HorizontalContentAlignment=Stretch and Padding=0,0,0,0, matching how regular item containers are configured.
  • Controversial change: MinHeight was also changed from {ThemeResource ListViewHeaderItemMinHeight} / {ThemeResource GridViewHeaderItemMinHeight} to 0. This is not strictly required for the width fix and may regress header height/touch target sizing.
  • The ContentPresenter within the ControlTemplate already binds HorizontalContentAlignment via {TemplateBinding}, so changing the style setter is sufficient to propagate stretch behavior.
  • The StackPanel in the ControlTemplate does not set HorizontalAlignment, which defaults to Stretch — this is consistent with the fix direction.
  • Prior automated test evaluation (evaluate-pr-tests) flagged: (1) missing GridItemsLayout test coverage, (2) AutomationIds with spaces may be brittle, (3) MinHeight change unexplained, (4) tight 2px tolerance on header-vs-footer width comparison.
  • Gate ✅ PASSED — tests fail without fix, pass with fix.
  • PR author noted this may be a breaking change and suggests targeting net11.0 branch.

Inline Review Comments (unresolved)

  1. MinHeight=0 for ListViewHeaderItem — unrelated to width fix, potential regression
  2. MinHeight=0 for GridViewHeaderItem — same concern
  3. AutomationId from bound Name value contains spaces (Header_Team A) — brittle for Appium
  4. AutomationId for footer (Footer_Team A) — same spaces concern
  5. Test uses these space-containing IDs in WaitForElement() — cross-platform reliability concern

Fix Candidates

# Source Approach Test Result Files Changed Notes
PR PR #34710 Change HorizontalContentAlignment to Stretch, Padding to 0, MinHeight to 0 on ListViewHeaderItem + GridViewHeaderItem ✅ PASSED (Gate) ItemsViewStyles.xaml Original PR; MinHeight change is controversial

🔧 Fix — Analysis & Comparison

Fix Candidates

# Source Approach Test Result Files Changed Notes
1 try-fix (claude-opus-4.6) Template-level override: hardcode HorizontalContentAlignment="Stretch" and Margin="0" on ContentPresenter in ControlTemplate, preserving all original setter values/theme resources ✅ PASS ItemsViewStyles.xaml (+4/-4 lines) More conservative than PR — preserves MinHeight and Padding theme resources
2 try-fix (claude-sonnet-4.6) Setter-only: change Padding→0,0,0,0 + HorizontalContentAlignment→Stretch on both header item styles; keep MinHeight at {ThemeResource} unchanged; ControlTemplate untouched ✅ PASS ItemsViewStyles.xaml (+2/-2 lines) Minimal fix — proves MinHeight=0 is NOT needed; cleaner than PR
3 try-fix (gpt-5.3-codex) Template StackPanel: set HorizontalAlignment="Stretch" on ControlTemplate's root StackPanel + ContentPresenter margin 0,8,0,0; no setter changes at all ✅ PASS ItemsViewStyles.xaml No setter modifications — purely template-level fix
4 try-fix (gpt-5.4, gemini unavailable) C# runtime hook attempt (no viable hook found) + fallback: only HorizontalContentAlignment→Stretch setter, leaving Padding and MinHeight at theme resources ❌ FAIL ItemsViewStyles.xaml Confirms Padding=0 is REQUIRED alongside HorizontalContentAlignment=Stretch; stretch alone leaves 24px gap
PR PR #34710 Change HorizontalContentAlignment→Stretch, Padding→0,0,0,0, MinHeight→0 on both ListViewHeaderItem + GridViewHeaderItem style setters ✅ PASSED (Gate) ItemsViewStyles.xaml Original PR; MinHeight change is controversial

Cross-Pollination

Model Round New Ideas? Details
claude-opus-4.6 2 No NO NEW IDEAS — solution space exhausted
claude-sonnet-4.6 2 No NO NEW IDEAS
gpt-5.3-codex 2 Yes NEW IDEA: Bind ItemWidth explicitly through ItemContentControl context — overly complex, fragile, not pursued
gpt-5.4 2 Yes NEW IDEA: Use GroupStyle.HeaderContainerStyle via selector to stretch header containers in C# — more invasive than needed, not pursued given simpler PASS alternatives

Exhausted: Yes — 3/4 models returned NO NEW IDEAS; 2 new ideas not viable given simpler passing solutions already found.

Selected Fix: Attempt 2 (Setter-only: Padding→0,0,0,0 + HorizontalContentAlignment→Stretch, keep MinHeight at theme resource) — Reason: Minimal, focused change to style setters only. Preserves theme resource MinHeight (addressing the main unresolved review comment). Proved by both Attempt 2 and Attempt 4's failure that both changes are required. The PR's MinHeight=0 change is unnecessary and was the primary concern in existing review feedback.


📋 Report — Final Recommendation

⚠️ Final Recommendation: REQUEST CHANGES

Phase Status

Phase Status Notes
Pre-Flight ✅ COMPLETE Issues #23038, #16824; Windows only; 3 files changed
Gate ✅ PASSED Windows — tests fail without fix, pass with fix
Try-Fix ✅ COMPLETE 4 attempts: 3 PASS, 1 FAIL; cross-pollination exhausted
Report ✅ COMPLETE

Summary

The PR correctly fixes a long-standing Windows CollectionView bug where group headers did not stretch to full width. The fix to HorizontalContentAlignment=Stretch and Padding=0 is valid and necessary. However, the PR includes one change that is not needed for the fix and creates a regression risk: setting MinHeight=0 for both ListViewHeaderItem and GridViewHeaderItem. Try-Fix exploration confirms a minimal setter-only fix (Padding + HorizontalContentAlignment, without touching MinHeight) passes all tests identically.

Selected Fix: PR's fix — except the MinHeight=0 change should be reverted.

Root Cause

Two style setters on ListViewHeaderItem and GridViewHeaderItem in ItemsViewStyles.xaml caused the bug:

  1. Padding="12,8,12,0" — the ControlTemplate binds ContentPresenter.Margin to Padding, shrinking the header horizontally by 24px
  2. HorizontalContentAlignment="Left" — prevents the ContentPresenter from stretching to fill available width

Both must be fixed together (Attempt 4's failure proved that changing only alignment is insufficient).

Fix Quality

What's good:

  • ✅ Correct root cause fix — both Padding=0 and HorizontalContentAlignment=Stretch are required and correctly applied to both ListViewHeaderItem and GridViewHeaderItem
  • ✅ Tests added with [Category(UITestCategories.CollectionView)] and proper structure
  • ✅ Gate passed — test verifies the fix empirically

What needs to change:

1. 🚨 Revert MinHeight=0 for both styles (critical)

The MinHeight change from {ThemeResource ListViewHeaderItemMinHeight} (and GridViewHeaderItemMinHeight) to 0 is not needed for the stretch fix. Attempt 2 proved this: changing only Padding and HorizontalContentAlignment passes the test with MinHeight kept at the theme resource value.

Setting MinHeight=0 risks:

  • Headers that are invisible/zero-height if content is empty or during layout passes
  • Touch target regression (WinUI theme values ensure minimum interactive area)
  • Potential visual regressions on different DPI scales

Suggested change (revert both MinHeight lines):

<!-- ListViewHeaderItem -->
<Setter Property="MinHeight" Value="{ThemeResource ListViewHeaderItemMinHeight}" />

<!-- GridViewHeaderItem -->
<Setter Property="MinHeight" Value="{ThemeResource GridViewHeaderItemMinHeight}" />

2. ⚠️ AutomationId values contain spaces (medium)

The HostApp binds AutomationId from group names: Header_Team A and Footer_Team A contain spaces. These IDs with spaces can be unreliable across Appium drivers. Prefer sanitized PascalCase IDs (e.g., HeaderTeamA, FooterTeamA) or use fixed string constants.

3. ⚠️ Missing GridItemsLayout test coverage (low)

The fix modifies both ListViewHeaderItem (used for linear layout) and GridViewHeaderItem (used for GridItemsLayout). The test only exercises the linear layout path. A second test case with ItemsLayout = new GridItemsLayout(2, ItemsLayoutOrientation.Vertical) would provide complete coverage.

4. ℹ️ Branch consideration

The PR description notes this may be a breaking change and asks about targeting net11.0. Since MinHeight is being changed from a theme value to 0, this could affect existing apps relying on the default header height. Reverting the MinHeight change would reduce the breaking surface and make the fix suitable for main.


@MauiBot MauiBot added s/agent-changes-requested AI agent recommends changes - found a better alternative or issues s/agent-reviewed PR was reviewed by AI agent workflow (full 4-phase review) labels Mar 30, 2026
…lish

- Revert MinHeight to theme resources for ListViewHeaderItem and
  GridViewHeaderItem (unrelated to the stretching fix)
- Use PascalCase AutomationIds without spaces for group headers/footers
  by adding a Key property to the team model
- Update XAML comments to reflect full scope of style customizations
- Remove redundant WaitForElement call and add tolerance explanation
- Update UI test to reference the sanitized AutomationIds

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 2, 2026

🧪 PR Test Evaluation

Overall Verdict: ✅ Tests are adequate

The test directly validates the fix — it verifies that group headers stretch to the full CollectionView width on Windows, which is exactly what the XAML style change achieves.

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

📊 Expand Full Evaluation

PR Test Evaluation Report

PR: #34710 — [Windows] CollectionView group header not stretching to full width
Test files evaluated: 2 (HostApp + NUnit)
Fix files: 1 (ItemsViewStyles.xaml)


Overall Verdict

Tests are adequate

The test exercises the exact behavior fixed by the PR: it measures group header and footer widths to confirm the header now stretches to full CollectionView width, which directly reflects the HorizontalContentAlignment: Left → Stretch style change.


1. Fix Coverage — ✅

The fix changes HorizontalContentAlignment from Left to Stretch (and zeroes Padding from 12,8,12,0) on both ListViewHeaderItem and GridViewHeaderItem styles in Windows XAML.

The test:

  • Measures the GroupedCollectionView width as a reference
  • Measures the HeaderTeamA and FooterTeamA widths
  • Asserts that the footer reaches the full CollectionView width (within 20px for scrollbar)
  • Asserts that the header matches the footer width (within 2px)

The test would fail without the fix (header stays Left-aligned, narrower than footer) and passes with it. ✅


2. Edge Cases & Gaps — ⚠️

Covered:

  • Happy path: first group header and footer stretch to full width

Missing:

  • Second group (TeamB): Only HeaderTeamA/FooterTeamA are measured; HeaderTeamB/FooterTeamB are not checked. Not critical, but would reinforce correctness.
  • GridView layout type: The fix also patches GridViewHeaderItem, but the test uses a CollectionView with default (list) layout. A grouped Grid layout variant is untested.
  • No GroupFooterTemplate case: The fix targets headers specifically; behavior when only a GroupHeaderTemplate is set (no footer for comparison) isn't tested.
  • Scrolled state: If the CollectionView has many items and is scrolled, does the header still stretch correctly? Not tested.

3. Test Type Appropriateness — ✅

Current: UI Test (Appium, NUnit)
Recommendation: UI test is appropriate here.

The bug is purely a visual layout issue — the header renders narrower than the list items on Windows. This requires actual Windows rendering to validate. Element width measurements via Appium (GetRect()) are the right tool. A device test could theoretically measure widths via the platform handler, but a UI test is a natural fit and consistent with other CollectionView issue tests in this repo.


4. Convention Compliance — ✅

The script reported 2 warnings for HeaderTeamA and FooterTeamA not found as literal strings in the HostApp — these are false positives. The AutomationIds are set via data binding:

label.SetBinding(Label.AutomationIdProperty, "Key", stringFormat: "Header{0}");
// Key = "TeamA" → AutomationId = "HeaderTeamA" at runtime ✅

All other conventions pass:

  • ✅ File naming: Issue23038.cs
  • [Issue(IssueTracker.Github, 23038, ..., PlatformAffected.UWP)] attribute present
  • ✅ Single [Category(UITestCategories.CollectionView)]
  • ✅ Inherits from _IssuesUITest
  • App.WaitForElement used before GetRect()
  • ✅ No Task.Delay / Thread.Sleep
  • ✅ No inline #if platform directives
  • ✅ No obsolete APIs

5. Flakiness Risk — ✅ Low

  • Within(20) tolerance for the footer-vs-CollectionView width comparison is reasonable (accounts for potential scrollbar width).
  • Within(2) for the header-vs-footer comparison is tight but appropriate (both are rendered in the same frame).
  • No screenshot assertions (no cursor-blink risk).
  • No animations or async timing issues — just layout measurement after WaitForElement.

Minor note: If the CollectionView hasn't fully laid out when GetRect() is called (possible on slow CI machines), there could be occasional flakiness. Adding WaitForElement for all three elements before any measurement (which the test already does) mitigates this.


6. Duplicate Coverage — ✅ No duplicates

No existing test covers this specific scenario (grouped CollectionView header width on Windows). Existing similar tests cover unrelated Windows platform behaviors.


7. Platform Scope — ✅

The fix is Windows-only (ItemsViewStyles.xaml in Platform/Windows/). The HostApp page uses PlatformAffected.UWP. The test is scoped correctly — no cross-platform concern here.


8. Assertion Quality — ✅

The assertion design is clever and avoids magic numbers:

  • Uses the footer as a baseline ("full-width") reference rather than hardcoding an expected pixel value.
  • The Within(20) tolerance for scrollbar is pragmatic.
  • The Within(2) for header-vs-footer comparison is specific enough to catch the bug.

This approach is resilient to different window sizes and DPI settings.


9. Fix-Test Alignment — ✅

Fix Test
Control ListViewHeaderItem / GridViewHeaderItem (Windows XAML) Grouped CollectionView header on Windows
Property HorizontalContentAlignment: Left → Stretch Header width == full CollectionView width
Would fail without fix? ✅ Yes — header stays narrow ✅ Yes — headerRect.Width != footerRect.Width

Recommendations

  1. (Optional) Add a second assertion for HeaderTeamB/FooterTeamB to verify all groups are affected, not just the first.
  2. (Optional) Consider a brief comment in the test explaining why the footer is used as the "full width" baseline — it's a non-obvious but correct approach.
  3. (Non-blocking) The GridViewHeaderItem style fix is untested (no test uses ItemsLayout = GridItemsLayout). Not strictly required since the root cause is the same, but worth noting.

Warning

⚠️ Firewall blocked 1 domain

The following domain was blocked by the firewall during workflow execution:

  • dc.services.visualstudio.com

To allow these domains, add them to the network.allowed list in your workflow frontmatter:

network:
  allowed:
    - defaults
    - "dc.services.visualstudio.com"

See Network Configuration for more information.

Note

🔒 Integrity filtering filtered 1 item

Integrity filtering activated and filtered the following item during workflow execution.
This happens when a tool call accesses a resource that does not meet the required integrity or secrecy level of the workflow.

🧪 Test evaluation by Evaluate PR Tests

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

copilot platform/windows s/agent-changes-requested AI agent recommends changes - found a better alternative or issues 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.

[Windows] GroupHeaderTemplate width smaller than ItemTemplate Group in a CollectionView on windows does not span horizontally

3 participants