[Windows] Fixed TimePicker CharacterSpacing issue#30533
Conversation
There was a problem hiding this comment.
Pull Request Overview
This PR fixes a bug where the Windows TimePicker control did not apply character spacing to its internal text blocks when loaded asynchronously. It enhances the UpdateCharacterSpacing extension to defer applying the spacing until after the control is loaded and adds helper methods to target each text block.
- Applies
CharacterSpacingtoHourTextBlock,MinuteTextBlock, andPeriodTextBlockwhen the control is ready. - Uses
OnLoadedto handle async loading scenarios. - Adds UI tests in both HostApp and Shared.Tests to visually verify the fix.
Reviewed Changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| src/Core/src/Platform/Windows/TimePickerExtensions.cs | Enhanced UpdateCharacterSpacing to wait for IsLoaded and apply spacing to each part. |
| src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue30199.cs | Added a UI test that waits for the TimePicker and captures a screenshot. |
| src/Controls/tests/TestCases.HostApp/Issues/Issue30199.cs | Created a HostApp page with a TimePicker configured to demonstrate the fix. |
Comments suppressed due to low confidence (2)
src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue30199.cs:21
- The UI test currently only waits for the element and takes a screenshot. Consider adding assertions that programmatically verify the CharacterSpacing on the internal TextBlocks or compare pixel spacing to ensure automated validation.
App.WaitForElement("timePicker");
src/Core/src/Platform/Windows/TimePickerExtensions.cs:23
- [nitpick] This public extension method lacks XML documentation. Adding a
<summary>explaining its purpose and behavior (especially the use ofOnLoaded) would improve discoverability and maintainability.
public static void UpdateCharacterSpacing(this TimePicker platformTimePicker, ITimePicker timePicker)
|
/azp run MAUI-UITests-public |
|
Azure Pipelines successfully started running 1 pipeline(s). |
|
/azp run MAUI-UITests-public |
|
Azure Pipelines successfully started running 1 pipeline(s). |
|
/rebase |
3d7f395 to
fa4a6ae
Compare
|
/rebase |
fa4a6ae to
e9208eb
Compare
|
/azp run MAUI-UITests-public |
|
Azure Pipelines successfully started running 1 pipeline(s). |
3a8bbe2 to
c7dc16a
Compare
52df190 to
e13d3cd
Compare
|
🚀 Dogfood this PR with:
curl -fsSL https://raw.githubusercontent.com/dotnet/maui/main/eng/scripts/get-maui-pr.sh | bash -s -- 30533Or
iex "& { $(irm https://raw.githubusercontent.com/dotnet/maui/main/eng/scripts/get-maui-pr.ps1) } 30533" |
🤖 AI Summary
📊 Review Session —
|
| # | Source | Approach | Test Result | Files Changed | Notes |
|---|---|---|---|---|---|
| PR | PR #30533 | Subscribe to Loaded event if not yet loaded, then walk visual tree to set CharacterSpacing on HourTextBlock/MinuteTextBlock/PeriodTextBlock |
✅ PASSED (Gate) | TimePickerExtensions.cs, TimePickerHandler.Windows.cs |
Has handler accumulation issue for rapid multiple updates before load |
🔧 Fix — Analysis & Comparison
Fix Candidates
| # | Source | Approach | Test Result | Files Changed | Notes |
|---|---|---|---|---|---|
| 1 | try-fix (claude-opus-4.6) | Move Loaded subscription to ConnectHandler/DisconnectHandler handler lifecycle; UpdateCharacterSpacing applies directly if already loaded |
✅ PASS | 2 files | Clean lifecycle, no accumulation, proper MAUI handler pattern |
| 2 | try-fix (claude-sonnet-4.6) | DispatcherQueue.TryEnqueue(DispatcherQueuePriority.Low, ...) to defer visual tree walk |
✅ PASS | 1 file | Simplest — no event subscriptions at all |
| 3 | try-fix (gpt-5.3-codex) | Synchronous ApplyTemplate() + UpdateLayout() + VisualTreeHelper recursive walk |
❌ FAIL | 2 files | Visual tree not ready synchronously during mapper execution |
| 4 | try-fix (gpt-5.4, gemini unavailable) | Subscribe to SizeChanged in ConnectHandler, unsubscribe after first fire |
✅ PASS | 2 files | Works but SizeChanged fires post-layout; needs one-shot guard |
| PR | PR #30533 | Closure-based Loaded event inside UpdateCharacterSpacing; walks visual tree on load |
✅ PASSED (Gate) | 2 files | Handler accumulation: multiple calls before load register multiple unclearable closures |
Cross-Pollination
| Model | Round | New Ideas? | Details |
|---|---|---|---|
| claude-opus-4.6 | 2 | Yes | Subclass TimePicker as MauiTimePicker, override OnApplyTemplate() |
| claude-sonnet-4.6 | 2 | Yes | Use FrameworkElement.FindName() after ApplyTemplate() — similar to failed attempt 3 |
| gpt-5.3-codex | 2 | Yes | One-shot LayoutUpdated callback — variant of Loaded/SizeChanged pattern |
| gpt-5.4 | 2 | Yes | MauiTimePicker subclass with binding — too large a refactor for this bug fix scope |
New ideas from round 2 are either too complex (subclass approach) or too similar to already-covered approaches (LayoutUpdated ≈ SizeChanged). No additional attempts warranted.
Exhausted: Yes — all distinct approaches within reasonable scope have been explored.
Selected Fix: Attempt 2 (DispatcherQueue) — DispatcherQueue.TryEnqueue(DispatcherQueuePriority.Low). Simplest implementation, only 1 file changed, no event lifecycle management needed, no handler accumulation possible. Attempt 1 (ConnectHandler) is a close second for most principled MAUI-pattern approach.
📋 Report — Final Recommendation
⚠️ Final Recommendation: REQUEST CHANGES
Phase Status
| Phase | Status | Notes |
|---|---|---|
| Pre-Flight | ✅ COMPLETE | Issue #30199, Windows TimePicker CharacterSpacing |
| Gate | ✅ PASSED | Windows platform, tests fail without fix, pass with fix |
| Try-Fix | ✅ COMPLETE | 4 attempts: 3 passing, 1 failing; simpler alternatives found |
| Report | ✅ COMPLETE |
Summary
PR #30533 fixes a real bug: TimePicker.CharacterSpacing on Windows doesn't propagate to the internal WinUI TextBlocks (HourTextBlock, MinuteTextBlock, PeriodTextBlock). The fix is functionally correct and the gate passed. However, the implementation has a handler accumulation bug and there are simpler alternatives found during try-fix exploration. Two alternative approaches (DispatcherQueue and ConnectHandler lifecycle) are cleaner and avoid the bug entirely.
Root Cause
WinUI's TimePicker.CharacterSpacing property does not automatically propagate to its internal control template TextBlocks. The property must be applied directly to those named TextBlocks after the control's visual tree is materialized (post-Loaded).
Fix Quality
Functional correctness: ✅ Works — gate passed.
Code quality issues:
-
Handler accumulation bug (
TimePickerExtensions.cs:32-41): The PR uses a lambda closure for theLoadedhandler. Each call toUpdateCharacterSpacingbefore the control is loaded appends a NEW handler that cannot be removed by subsequent calls. IfCharacterSpacingis updated N times before the control loads, N handlers fire onLoaded. Compare withDatePickerExtensions.cswhich uses a local function pattern that correctly removes the previous handler before registering:platformDatePicker.Loaded -= OnDatePickerLoaded; // removes previous void OnDatePickerLoaded(object sender, RoutedEventArgs e) { ... } // local func (stable reference) platformDatePicker.Loaded += OnDatePickerLoaded;
-
Unnecessary
usingin handler file:using Microsoft.UI.Xaml;was added toTimePickerHandler.Windows.csbutTimePickerHandler.Windows.csdoes not directly reference any types from that namespace — the Loaded handler is inTimePickerExtensions.cs. -
Test HostApp: string issue number:
[Issue(IssueTracker.Github, "30199", ...)]uses a string literal. The convention for numeric GitHub issues is theintoverload:[Issue(IssueTracker.Github, 30199, ...)]. -
Missing newline at end of file: Both
TestCases.HostApp/Issues/Issue30199.csandTestCases.Shared.Tests/Tests/Issues/Issue30199.csare missing a trailing newline.
Better Alternatives Found
Recommended — Attempt 2 (DispatcherQueue, ✅ PASS, 1 file only):
public static void UpdateCharacterSpacing(this TimePicker platformTimePicker, ITimePicker timePicker)
{
platformTimePicker.CharacterSpacing = timePicker.CharacterSpacing.ToEm();
platformTimePicker.DispatcherQueue.TryEnqueue(DispatcherQueuePriority.Low, () =>
{
ApplyCharacterSpacingToTextBlocks(platformTimePicker);
});
}Only TimePickerExtensions.cs needs to change. No event subscriptions, no accumulation risk.
Alternative — Attempt 1 (ConnectHandler lifecycle, ✅ PASS, follows MAUI patterns):
Subscribe to Loaded once in ConnectHandler, unsubscribe in DisconnectHandler. UpdateCharacterSpacing sets the property and calls ApplyCharacterSpacingToTextBlocks() directly if already loaded. Proper MAUI handler lifecycle management with zero accumulation risk.
Minimum fix to current PR approach (if author prefers to keep same structure): Follow the DatePickerExtensions.cs local-function pattern — use a local function (not a lambda closure) so it can be de-registered with -= before re-registering.
Minor Test Note
The test only calls VerifyScreenshot() on the initial state of the TimePicker. This is a valid regression guard, but it does not verify dynamic updates (e.g., changing CharacterSpacing after the page is loaded). This is acceptable for the current scope.
🚦 Gate — Test Before and After Fix
🚦 Gate Session —
|
| Test | Without Fix (expect FAIL) | With Fix (expect PASS) |
|---|---|---|
🖥️ Issue30199 Issue30199 |
✅ FAIL — 573s | ✅ PASS — 469s |
🔴 Without fix — 🖥️ Issue30199: FAIL ✅ · 573s
Determining projects to restore...
Restored D:\a\1\s\src\Graphics\src\Graphics\Graphics.csproj (in 41.63 sec).
Restored D:\a\1\s\src\Controls\src\Xaml\Controls.Xaml.csproj (in 41.7 sec).
Restored D:\a\1\s\src\Essentials\src\Essentials.csproj (in 23 ms).
Restored D:\a\1\s\src\Core\src\Core.csproj (in 91 ms).
Restored D:\a\1\s\src\Graphics\src\Graphics.Win2D\Graphics.Win2D.csproj (in 192 ms).
Restored D:\a\1\s\src\Core\maps\src\Maps.csproj (in 4.56 sec).
Restored D:\a\1\s\src\Controls\src\Core\Controls.Core.csproj (in 26 ms).
Restored D:\a\1\s\src\Controls\Maps\src\Controls.Maps.csproj (in 25 ms).
Restored D:\a\1\s\src\Controls\Foldable\src\Controls.Foldable.csproj (in 26 ms).
Restored D:\a\1\s\src\BlazorWebView\src\Maui\Microsoft.AspNetCore.Components.WebView.Maui.csproj (in 58 ms).
Restored D:\a\1\s\src\Controls\tests\TestCases.HostApp\Controls.TestCases.HostApp.csproj (in 5.06 sec).
3 of 14 projects are up-to-date for restore.
##vso[build.updatebuildnumber]10.0.60-ci+azdo.13817167
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.13817167
##vso[build.updatebuildnumber]10.0.60-ci+azdo.13817167
Graphics.Win2D -> D:\a\1\s\artifacts\bin\Graphics.Win2D\Debug\net10.0-windows10.0.19041.0\Microsoft.Maui.Graphics.Win2D.WinUI.Desktop.dll
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.13817167
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.13817167
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.13817167
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.13817167
##vso[build.updatebuildnumber]10.0.60-ci+azdo.13817167
Controls.Foldable -> D:\a\1\s\artifacts\bin\Controls.Foldable\Debug\net10.0-windows10.0.19041.0\Microsoft.Maui.Controls.Foldable.dll
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.13817167
Controls.Maps -> D:\a\1\s\artifacts\bin\Controls.Maps\Debug\net10.0-windows10.0.19041.0\Microsoft.Maui.Controls.Maps.dll
##vso[build.updatebuildnumber]10.0.60-ci+azdo.13817167
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.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:06:02.81
Determining projects to restore...
Restored D:\a\1\s\src\Controls\tests\CustomAttributes\Controls.CustomAttributes.csproj (in 894 ms).
Restored D:\a\1\s\src\TestUtils\src\VisualTestUtils\VisualTestUtils.csproj (in 3 ms).
Restored D:\a\1\s\src\TestUtils\src\VisualTestUtils.MagickNet\VisualTestUtils.MagickNet.csproj (in 6.08 sec).
Restored D:\a\1\s\src\TestUtils\src\UITest.NUnit\UITest.NUnit.csproj (in 3.02 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 2 ms).
Restored D:\a\1\s\src\Controls\tests\TestCases.WinUI.Tests\Controls.TestCases.WinUI.Tests.csproj (in 10.76 sec).
Restored D:\a\1\s\src\TestUtils\src\UITest.Analyzers\UITest.Analyzers.csproj (in 5.91 sec).
7 of 15 projects are up-to-date for restore.
##vso[build.updatebuildnumber]10.0.60-ci+azdo.13817167
Graphics -> D:\a\1\s\artifacts\bin\Graphics\Debug\net10.0\Microsoft.Maui.Graphics.dll
##vso[build.updatebuildnumber]10.0.60-ci+azdo.13817167
Essentials -> D:\a\1\s\artifacts\bin\Essentials\Debug\net10.0\Microsoft.Maui.Essentials.dll
##vso[build.updatebuildnumber]10.0.60-ci+azdo.13817167
Controls.CustomAttributes -> D:\a\1\s\artifacts\bin\Controls.CustomAttributes\Debug\net10.0\Controls.CustomAttributes.dll
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.13817167
Controls.Core -> D:\a\1\s\artifacts\bin\Controls.Core\Debug\net10.0\Microsoft.Maui.Controls.dll
VisualTestUtils -> D:\a\1\s\artifacts\bin\VisualTestUtils\Debug\netstandard2.0\VisualTestUtils.dll
UITest.Core -> D:\a\1\s\artifacts\bin\UITest.Core\Debug\net10.0\UITest.Core.dll
VisualTestUtils.MagickNet -> D:\a\1\s\artifacts\bin\VisualTestUtils.MagickNet\Debug\netstandard2.0\VisualTestUtils.MagickNet.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
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
>>>>> 4/13/2026 11:58:24 AM FixtureSetup for Issue30199(Windows)
>>>>> 4/13/2026 11:58:32 AM Issue30199TimePickerCharacterSpacingShouldApply Start
>>>>> 4/13/2026 11:58:35 AM Issue30199TimePickerCharacterSpacingShouldApply Stop
>>>>> 4/13/2026 11:58:35 AM Log types:
Failed Issue30199TimePickerCharacterSpacingShouldApply [2 s]
Error Message:
VisualTestUtils.VisualTestFailedException :
Snapshot different than baseline: Issue30199TimePickerCharacterSpacingShouldApply.png (0.63% 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, Boolean includeTitleBar) in /_/src/Controls/tests/TestCases.Shared.Tests/UITest.cs:line 309
at Microsoft.Maui.TestCases.Tests.Issues.Issue30199.Issue30199TimePickerCharacterSpacingShouldApply() in /_/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue30199.cs:line 22
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
[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.WinUI.Tests
[xUnit.net 00:00:00.34] Discovered: Controls.TestCases.WinUI.Tests
Total tests: 1
Failed: 1
Test Run Failed.
Total time: 33.0624 Seconds
🟢 With fix — 🖥️ Issue30199: PASS ✅ · 469s
Determining projects to restore...
All projects are up-to-date for restore.
##vso[build.updatebuildnumber]10.0.60-ci+azdo.13817167
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.13817167
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.13817167
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.13817167
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.13817167
##vso[build.updatebuildnumber]10.0.60-ci+azdo.13817167
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.13817167
##vso[build.updatebuildnumber]10.0.60-ci+azdo.13817167
##vso[build.updatebuildnumber]10.0.60-ci+azdo.13817167
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.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
Maps -> D:\a\1\s\artifacts\bin\Maps\Debug\net10.0-windows10.0.19041.0\Microsoft.Maui.Maps.dll
##vso[build.updatebuildnumber]10.0.60-ci+azdo.13817167
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:50.68
Determining projects to restore...
All projects are up-to-date for restore.
##vso[build.updatebuildnumber]10.0.60-ci+azdo.13817167
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.13817167
Essentials -> D:\a\1\s\artifacts\bin\Essentials\Debug\net10.0\Microsoft.Maui.Essentials.dll
##vso[build.updatebuildnumber]10.0.60-ci+azdo.13817167
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.13817167
Controls.Core -> D:\a\1\s\artifacts\bin\Controls.Core\Debug\net10.0\Microsoft.Maui.Controls.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.Core -> D:\a\1\s\artifacts\bin\UITest.Core\Debug\net10.0\UITest.Core.dll
UITest.NUnit -> D:\a\1\s\artifacts\bin\UITest.NUnit\Debug\net10.0\UITest.NUnit.dll
UITest.Appium -> D:\a\1\s\artifacts\bin\UITest.Appium\Debug\net10.0\UITest.Appium.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
>>>>> 4/13/2026 12:06:15 PM FixtureSetup for Issue30199(Windows)
>>>>> 4/13/2026 12:06:23 PM Issue30199TimePickerCharacterSpacingShouldApply Start
>>>>> 4/13/2026 12:06:24 PM Issue30199TimePickerCharacterSpacingShouldApply Stop
Passed Issue30199TimePickerCharacterSpacingShouldApply [1 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.12] Discovering: Controls.TestCases.WinUI.Tests
[xUnit.net 00:00:00.32] Discovered: Controls.TestCases.WinUI.Tests
Test Run Successful.
Total tests: 1
Passed: 1
Total time: 25.2840 Seconds
📁 Fix files reverted (4 files)
eng/pipelines/ci-copilot.ymleng/pipelines/ci-official.ymlsrc/Core/src/Handlers/TimePicker/TimePickerHandler.Windows.cssrc/Core/src/Platform/Windows/TimePickerExtensions.cs
kubaflo
left a comment
There was a problem hiding this comment.
Could you please review the AI's summary?
Backport of dotnet#34801 to main. The official pack pipeline doesn't need iOS simulators — it only builds and packs NuGet packages. The simulator install step is timing out on macOS agents, blocking BAR build production. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
29c9af7 to
ec75540
Compare
|
@kubaflo I have reviewed the AI summary and the current handler-based approach is correct — WinUI re-creates template children on every visual tree re-entry, making a persistent Loaded subscription the right choice over a one-time lambda. |
kubaflo
left a comment
There was a problem hiding this comment.
Can you please try this:
public static void UpdateCharacterSpacing(this TimePicker platformTimePicker, ITimePicker timePicker)
{
platformTimePicker.CharacterSpacing = timePicker.CharacterSpacing.ToEm();
if (!platformTimePicker.IsLoaded)
{
RoutedEventHandler? onLoaded = null;
onLoaded = (s, e) =>
{
platformTimePicker.Loaded -= onLoaded;
UpdateCharacterSpacingInTimePicker(platformTimePicker);
};
platformTimePicker.Loaded += onLoaded;
return;
}
UpdateCharacterSpacingInTimePicker(platformTimePicker);
}
@kubaflo As suggested I have modified the changes |
<!-- Please let the below note in for people that find this PR --> > [!NOTE] > Are you waiting for the changes in this PR to be merged? > It would be very helpful if you could [test the resulting artifacts](https://github.com/dotnet/maui/wiki/Testing-PR-Builds) from this PR and let us know in a comment if this change resolves your issue. Thank you! <!-- !!!!!!! MAIN IS THE ONLY ACTIVE BRANCH. MAKE SURE THIS PR IS TARGETING MAIN. !!!!!!! --> ### Root Cause: The UpdateCharacterSpacing method didn't apply spacing to the individual text blocks in TimePicker ### Description of Change Enhanced the UpdateCharacterSpacing method to apply CharacterSpacing to individual text blocks (HourTextBlock, MinuteTextBlock, and PeriodTextBlock) within the TimePicker. This ensures the property works correctly even when the control is loaded asynchronously. <!-- Enter description of the fix in this section --> ### Issues Fixed <!-- Please make sure that there is a bug logged for the issue being fixed. The bug should describe the problem and how to reproduce it. --> Fixes #30199 ### Tested the behaviour in the following platforms - [x] Windows - [x] Android - [x] iOS - [ ] Mac ### Screenshot | Before Issue Fix | After Issue Fix | |----------|----------| | <img width="1217" height="915" alt="beforeFix30199" src="https://github.com/user-attachments/assets/0b955bf7-5e75-48d3-8455-3fe0cf2c0c5b"> | <img width="1261" height="946" alt="Screenshot 2025-07-10 155258" src="https://github.com/user-attachments/assets/5d2d8a34-fb01-4c58-9fef-be3ad10b7cb0"> | <!-- Are you targeting main? All PRs should target the main branch unless otherwise noted. --> ---------
<!-- Please let the below note in for people that find this PR --> > [!NOTE] > Are you waiting for the changes in this PR to be merged? > It would be very helpful if you could [test the resulting artifacts](https://github.com/dotnet/maui/wiki/Testing-PR-Builds) from this PR and let us know in a comment if this change resolves your issue. Thank you! <!-- !!!!!!! MAIN IS THE ONLY ACTIVE BRANCH. MAKE SURE THIS PR IS TARGETING MAIN. !!!!!!! --> ### Root Cause: The UpdateCharacterSpacing method didn't apply spacing to the individual text blocks in TimePicker ### Description of Change Enhanced the UpdateCharacterSpacing method to apply CharacterSpacing to individual text blocks (HourTextBlock, MinuteTextBlock, and PeriodTextBlock) within the TimePicker. This ensures the property works correctly even when the control is loaded asynchronously. <!-- Enter description of the fix in this section --> ### Issues Fixed <!-- Please make sure that there is a bug logged for the issue being fixed. The bug should describe the problem and how to reproduce it. --> Fixes dotnet#30199 ### Tested the behaviour in the following platforms - [x] Windows - [x] Android - [x] iOS - [ ] Mac ### Screenshot | Before Issue Fix | After Issue Fix | |----------|----------| | <img width="1217" height="915" alt="beforeFix30199" src="https://github.com/user-attachments/assets/0b955bf7-5e75-48d3-8455-3fe0cf2c0c5b"> | <img width="1261" height="946" alt="Screenshot 2025-07-10 155258" src="https://github.com/user-attachments/assets/5d2d8a34-fb01-4c58-9fef-be3ad10b7cb0"> | <!-- Are you targeting main? All PRs should target the main branch unless otherwise noted. --> ---------
<!-- Please let the below note in for people that find this PR --> > [!NOTE] > Are you waiting for the changes in this PR to be merged? > It would be very helpful if you could [test the resulting artifacts](https://github.com/dotnet/maui/wiki/Testing-PR-Builds) from this PR and let us know in a comment if this change resolves your issue. Thank you! <!-- !!!!!!! MAIN IS THE ONLY ACTIVE BRANCH. MAKE SURE THIS PR IS TARGETING MAIN. !!!!!!! --> ### Root Cause: The UpdateCharacterSpacing method didn't apply spacing to the individual text blocks in TimePicker ### Description of Change Enhanced the UpdateCharacterSpacing method to apply CharacterSpacing to individual text blocks (HourTextBlock, MinuteTextBlock, and PeriodTextBlock) within the TimePicker. This ensures the property works correctly even when the control is loaded asynchronously. <!-- Enter description of the fix in this section --> ### Issues Fixed <!-- Please make sure that there is a bug logged for the issue being fixed. The bug should describe the problem and how to reproduce it. --> Fixes #30199 ### Tested the behaviour in the following platforms - [x] Windows - [x] Android - [x] iOS - [ ] Mac ### Screenshot | Before Issue Fix | After Issue Fix | |----------|----------| | <img width="1217" height="915" alt="beforeFix30199" src="https://github.com/user-attachments/assets/0b955bf7-5e75-48d3-8455-3fe0cf2c0c5b"> | <img width="1261" height="946" alt="Screenshot 2025-07-10 155258" src="https://github.com/user-attachments/assets/5d2d8a34-fb01-4c58-9fef-be3ad10b7cb0"> | <!-- Are you targeting main? All PRs should target the main branch unless otherwise noted. --> ---------
<!-- Please let the below note in for people that find this PR --> > [!NOTE] > Are you waiting for the changes in this PR to be merged? > It would be very helpful if you could [test the resulting artifacts](https://github.com/dotnet/maui/wiki/Testing-PR-Builds) from this PR and let us know in a comment if this change resolves your issue. Thank you! <!-- !!!!!!! MAIN IS THE ONLY ACTIVE BRANCH. MAKE SURE THIS PR IS TARGETING MAIN. !!!!!!! --> ### Root Cause: The UpdateCharacterSpacing method didn't apply spacing to the individual text blocks in TimePicker ### Description of Change Enhanced the UpdateCharacterSpacing method to apply CharacterSpacing to individual text blocks (HourTextBlock, MinuteTextBlock, and PeriodTextBlock) within the TimePicker. This ensures the property works correctly even when the control is loaded asynchronously. <!-- Enter description of the fix in this section --> ### Issues Fixed <!-- Please make sure that there is a bug logged for the issue being fixed. The bug should describe the problem and how to reproduce it. --> Fixes #30199 ### Tested the behaviour in the following platforms - [x] Windows - [x] Android - [x] iOS - [ ] Mac ### Screenshot | Before Issue Fix | After Issue Fix | |----------|----------| | <img width="1217" height="915" alt="beforeFix30199" src="https://github.com/user-attachments/assets/0b955bf7-5e75-48d3-8455-3fe0cf2c0c5b"> | <img width="1261" height="946" alt="Screenshot 2025-07-10 155258" src="https://github.com/user-attachments/assets/5d2d8a34-fb01-4c58-9fef-be3ad10b7cb0"> | <!-- Are you targeting main? All PRs should target the main branch unless otherwise noted. --> ---------
<!-- Please let the below note in for people that find this PR --> > [!NOTE] > Are you waiting for the changes in this PR to be merged? > It would be very helpful if you could [test the resulting artifacts](https://github.com/dotnet/maui/wiki/Testing-PR-Builds) from this PR and let us know in a comment if this change resolves your issue. Thank you! <!-- !!!!!!! MAIN IS THE ONLY ACTIVE BRANCH. MAKE SURE THIS PR IS TARGETING MAIN. !!!!!!! --> ### Root Cause: The UpdateCharacterSpacing method didn't apply spacing to the individual text blocks in TimePicker ### Description of Change Enhanced the UpdateCharacterSpacing method to apply CharacterSpacing to individual text blocks (HourTextBlock, MinuteTextBlock, and PeriodTextBlock) within the TimePicker. This ensures the property works correctly even when the control is loaded asynchronously. <!-- Enter description of the fix in this section --> ### Issues Fixed <!-- Please make sure that there is a bug logged for the issue being fixed. The bug should describe the problem and how to reproduce it. --> Fixes #30199 ### Tested the behaviour in the following platforms - [x] Windows - [x] Android - [x] iOS - [ ] Mac ### Screenshot | Before Issue Fix | After Issue Fix | |----------|----------| | <img width="1217" height="915" alt="beforeFix30199" src="https://github.com/user-attachments/assets/0b955bf7-5e75-48d3-8455-3fe0cf2c0c5b"> | <img width="1261" height="946" alt="Screenshot 2025-07-10 155258" src="https://github.com/user-attachments/assets/5d2d8a34-fb01-4c58-9fef-be3ad10b7cb0"> | <!-- Are you targeting main? All PRs should target the main branch unless otherwise noted. --> ---------
<!-- Please let the below note in for people that find this PR --> > [!NOTE] > Are you waiting for the changes in this PR to be merged? > It would be very helpful if you could [test the resulting artifacts](https://github.com/dotnet/maui/wiki/Testing-PR-Builds) from this PR and let us know in a comment if this change resolves your issue. Thank you! <!-- !!!!!!! MAIN IS THE ONLY ACTIVE BRANCH. MAKE SURE THIS PR IS TARGETING MAIN. !!!!!!! --> ### Root Cause: The UpdateCharacterSpacing method didn't apply spacing to the individual text blocks in TimePicker ### Description of Change Enhanced the UpdateCharacterSpacing method to apply CharacterSpacing to individual text blocks (HourTextBlock, MinuteTextBlock, and PeriodTextBlock) within the TimePicker. This ensures the property works correctly even when the control is loaded asynchronously. <!-- Enter description of the fix in this section --> ### Issues Fixed <!-- Please make sure that there is a bug logged for the issue being fixed. The bug should describe the problem and how to reproduce it. --> Fixes #30199 ### Tested the behaviour in the following platforms - [x] Windows - [x] Android - [x] iOS - [ ] Mac ### Screenshot | Before Issue Fix | After Issue Fix | |----------|----------| | <img width="1217" height="915" alt="beforeFix30199" src="https://github.com/user-attachments/assets/0b955bf7-5e75-48d3-8455-3fe0cf2c0c5b"> | <img width="1261" height="946" alt="Screenshot 2025-07-10 155258" src="https://github.com/user-attachments/assets/5d2d8a34-fb01-4c58-9fef-be3ad10b7cb0"> | <!-- Are you targeting main? All PRs should target the main branch unless otherwise noted. --> ---------
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!
Root Cause:
The UpdateCharacterSpacing method didn't apply spacing to the individual text blocks in TimePicker
Description of Change
Enhanced the UpdateCharacterSpacing method to apply CharacterSpacing to individual text blocks (HourTextBlock, MinuteTextBlock, and PeriodTextBlock) within the TimePicker. This ensures the property works correctly even when the control is loaded asynchronously.
Issues Fixed
Fixes #30199
Tested the behaviour in the following platforms
Screenshot