Skip to content

Fix DatePicker CharacterSpacing property not working on Windows platform #30139

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 5 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 63 additions & 0 deletions src/Controls/tests/TestCases.HostApp/Issues/Issue30066.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
using Microsoft.Maui.Controls;
using System;

namespace Maui.Controls.Sample.Issues;

[Issue(IssueTracker.Github, "30066", "DatePicker CharacterSpacing Property Not Working on Windows", PlatformAffected.UWP)]
public class Issue30066 : TestShell
{
protected override void Init()
{
var shellContent = new ShellContent
{
Title = "DatePicker CharacterSpacing Test",
Content = new Issue30066ContentPage() { Title = "DatePicker CharacterSpacing Test" }
};

Items.Add(shellContent);
}

class Issue30066ContentPage : ContentPage
{
public Issue30066ContentPage()
{
var datePicker = new DatePicker
{
Date = DateTime.Today,
CharacterSpacing = 10,
AutomationId = "TestDatePicker",
HorizontalOptions = LayoutOptions.Center,
VerticalOptions = LayoutOptions.Center,
};

var label = new Label
{
Text = "The DatePicker above should have character spacing of 10 applied to its text.",
HorizontalOptions = LayoutOptions.Center,
Margin = new Thickness(20),
HorizontalTextAlignment = TextAlignment.Center
};

var button = new Button
{
Text = "Change Character Spacing to 20",
AutomationId = "ChangeSpacingButton",
HorizontalOptions = LayoutOptions.Center,
Margin = new Thickness(0, 20)
};

button.Clicked += (s, e) =>
{
datePicker.CharacterSpacing = datePicker.CharacterSpacing == 10 ? 20 : 10;
button.Text = $"Change Character Spacing to {(datePicker.CharacterSpacing == 10 ? 20 : 10)}";
};

Content = new StackLayout
{
Children = { datePicker, label, button },
Spacing = 20,
Margin = new Thickness(20)
};
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
using NUnit.Framework;
using UITest.Appium;
using UITest.Core;

namespace Microsoft.Maui.TestCases.Tests.Issues;

public class Issue30066 : _IssuesUITest
{
public Issue30066(TestDevice device)
: base(device)
{
}

public override string Issue => "DatePicker CharacterSpacing Property Not Working on Windows";

[Test]
[Category(UITestCategories.DatePicker)]
public void DatePickerCharacterSpacingShouldApply()
{
App.WaitForElement("TestDatePicker");

// Take a screenshot to verify character spacing is applied
// On Windows, the DatePicker text should show increased character spacing
VerifyScreenshot();
}

[Test]
[Category(UITestCategories.DatePicker)]
public void DatePickerCharacterSpacingCanChange()
{
App.WaitForElement("TestDatePicker");
App.WaitForElement("ChangeSpacingButton");

// Change the character spacing by clicking the button
App.Tap("ChangeSpacingButton");

// Take a screenshot to verify the character spacing changed
VerifyScreenshot();
}
}
16 changes: 16 additions & 0 deletions src/Core/src/Platform/Windows/CharacterSpacingExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,21 @@ public static int ToEm(this double pt)
{
return Convert.ToInt32(pt * 0.0624f * 1000); // Coefficient for converting Pt to Em. The value is uniform spacing between characters, in units of 1/1000 of an em.
}

/// <summary>
/// Converts character spacing from Em units back to the original double value.
/// </summary>
/// <param name="emValue">The character spacing value in Em units (1/1000 of an em)</param>
/// <returns>The original character spacing value in points</returns>
/// <remarks>
/// This method performs the inverse operation of ToEm().
/// Em units are defined as 1/1000 of an em, where the conversion coefficient is 0.0624.
/// The calculation reverses: originalValue * 0.0624 * 1000 = emValue
/// So: originalValue = emValue / (0.0624 * 1000)
/// </remarks>
public static double FromEm(this int emValue)
{
return emValue / (0.0624 * 1000);
}
}
}
30 changes: 29 additions & 1 deletion src/Core/src/Platform/Windows/DatePickerExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Linq;
using Microsoft.Maui.Graphics;
using Microsoft.UI.Xaml.Controls;
using WBrush = Microsoft.UI.Xaml.Media.Brush;
Expand Down Expand Up @@ -38,7 +39,34 @@ public static void UpdateMaximumDate(this CalendarDatePicker platformDatePicker,

public static void UpdateCharacterSpacing(this CalendarDatePicker platformDatePicker, IDatePicker datePicker)
{
platformDatePicker.CharacterSpacing = datePicker.CharacterSpacing.ToEm();
// Store the character spacing value to apply it when ready
var characterSpacing = datePicker.CharacterSpacing;

// Apply immediately if loaded, otherwise wait for load
if (platformDatePicker.IsLoaded)
{
ApplyCharacterSpacingToTextBlocks(platformDatePicker, characterSpacing);
}
else
{
// Wait for the control to load, then apply character spacing
platformDatePicker.OnLoaded(() => ApplyCharacterSpacingToTextBlocks(platformDatePicker, characterSpacing));
}
}

static void ApplyCharacterSpacingToTextBlocks(CalendarDatePicker platformDatePicker, double characterSpacing)
{
// Find all TextBlock elements within the CalendarDatePicker and apply character spacing
var textBlocks = platformDatePicker.GetChildren<Microsoft.UI.Xaml.Controls.TextBlock>();
Copy link
Contributor

@sheiksyedm sheiksyedm Jun 23, 2025

Choose a reason for hiding this comment

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

@copilot Can you directly access the placeholder TextBlock element in the CalendarDatePicker like below?
platformDatePicker.GetDescendantByName("DateText");

Copy link
Contributor

Choose a reason for hiding this comment

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

Like in other parts of the framework, can use the extension method

internal static T? GetDescendantByName<T>(this DependencyObject parent, string elementName) where T : DependencyObject

var characterSpacingEm = characterSpacing.ToEm();

foreach (var textBlock in textBlocks)
{
if (textBlock is not null)
{
textBlock.CharacterSpacing = characterSpacingEm;
}
}
}

public static void UpdateFont(this CalendarDatePicker platformDatePicker, IDatePicker datePicker, IFontManager fontManager) =>
Expand Down
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
#nullable enable
static Microsoft.Maui.Platform.CharacterSpacingExtensions.FromEm(this int emValue) -> double
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using System.Threading.Tasks;
using Microsoft.Maui.Controls;
using Microsoft.Maui.DeviceTests.Stubs;
using Microsoft.Maui.Platform;
using Microsoft.UI.Xaml.Controls;
using Xunit;

Expand Down Expand Up @@ -89,5 +90,53 @@ Color GetNativeTextColor(DatePickerHandler datePickerHandler)

return null;
}

[Fact]
public async Task CharacterSpacingInitializesCorrectly()
Copy link
Contributor

Choose a reason for hiding this comment

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

Could you create another test to validate updates? CharacterSpacingUpdatesCorrectly passing different CharacterSpacing values.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Added CharacterSpacingUpdatesCorrectly test that validates character spacing updates with different values (5.0 to 15.0) in commit ba3447c.

{
var datePicker = new DatePickerStub()
{
Date = DateTime.Today,
CharacterSpacing = 10
};

await ValidatePropertyInitValue(datePicker, () => datePicker.CharacterSpacing, GetNativeCharacterSpacing, 10.0);
}

double GetNativeCharacterSpacing(DatePickerHandler datePickerHandler)
{
var platformDatePicker = GetNativeDatePicker(datePickerHandler);

// Since CalendarDatePicker doesn't have CharacterSpacing property directly,
// we need to check if it was applied to internal TextBlock elements
var textBlocks = platformDatePicker.GetChildren<Microsoft.UI.Xaml.Controls.TextBlock>();
foreach (var textBlock in textBlocks)
{
if (textBlock is not null && textBlock.CharacterSpacing > 0)
{
// Convert back from Em to original value using the extension method
return textBlock.CharacterSpacing.FromEm();
}
}

return 0.0;
}

[Fact]
public async Task CharacterSpacingUpdatesCorrectly()
{
var datePicker = new DatePickerStub()
{
Date = DateTime.Today,
CharacterSpacing = 5
};

await ValidatePropertyUpdatesValue(
datePicker,
nameof(IDatePicker.CharacterSpacing),
GetNativeCharacterSpacing,
5.0,
15.0);
}
}
}