Skip to content

[Feature] Wide LetterSpacing Support #20119

@jsuarezruiz

Description

@jsuarezruiz

Is your feature request related to a problem? Please describe.

Currently, the LetterSpacing property is available in TextBlock but not in controls like Button.

Describe the solution you'd like

Abstract

Text formatting consistency is essential. This proposal the location of the LetterSpacing property into TemplatedControl, making it available to all controls capable of presenting text.
This aligns the property with existing text-related APIs (FontFamily, FontSize, FontWeight) and ensures a unified model across all templated controls.

  • Provide a consistent text-formatting API across the control hierarchy.
  • Enable LetterSpacing on all templated text-displaying controls.
  • Maintain full backward compatibility.
  • Minimize template changes through use of attached property syntax.

Requirements

  • Consistent property location. All core text properties already live on TemplatedControl. LetterSpacing must follow this pattern for predictability.
  • Template-friendly design. Use attached property syntax (TextBlock.LetterSpacing="{TemplateBinding LetterSpacing}") .
  • Broad control availability. Every TemplatedControl descendant including Label, ToolTip, CheckBox, RadioButton, and others should gain LetterSpacing property.
  • No breaking changes. Existing code must continue to work without modification. TextBlock's existing LetterSpacing property remains unchanged.
  • Full test coverage. Unit tests must validate default values, inheritance, negative values, and template propagation between other scenarios.

API

Define on TextElement:

/// <summary>
/// Base class for text elements.
/// </summary>
public class TextElement : Animatable
{
    /// <summary>
    /// Defines the LetterSpacing attached property.
    /// </summary>
    /// <remarks>
    /// This is an inherited attached property that defines letter spacing for text.
    /// Letter spacing is specified in pixels. Default value is 0 (normal spacing).
    /// Positive values increase spacing between characters.
    /// Negative values decrease spacing between characters.
    /// </remarks>
    public static readonly AttachedProperty<double> LetterSpacingProperty =
        AvaloniaProperty.RegisterAttached<TextElement, Control, double>(
            name: "LetterSpacing",
            defaultValue: 0.0,
            inherits: true);
    
    /// <summary>
    /// Gets the value of the LetterSpacing attached property on the specified control.
    /// </summary>
    /// <param name="control">The control.</param>
    /// <returns>The letter spacing value in pixels.</returns>
    public static double GetLetterSpacing(Control control) =>
        control.GetValue(LetterSpacingProperty);
    
    /// <summary>
    /// Sets the value of the LetterSpacing attached property on the specified control.
    /// </summary>
    /// <param name="control">The control.</param>
    /// <param name="value">The letter spacing value in pixels.</param>
    public static void SetLetterSpacing(Control control, double value) =>
        control.SetValue(LetterSpacingProperty, value);
}

AddOwner on TextBlock:

/// <summary>
/// A control that displays a block of text.
/// </summary>
public class TextBlock : Control
{
    /// <summary>
    /// Defines the <see cref="LetterSpacing"/> property.
    /// </summary>
    /// <remarks>
    /// This property uses AddOwner to share the same property definition as 
    /// TextElement.LetterSpacingProperty, ensuring consistent behavior across the framework.
    /// </remarks>
    public static readonly StyledProperty<double> LetterSpacingProperty =
        TextElement.LetterSpacingProperty.AddOwner<TextBlock>();
    
    /// <summary>
    /// Gets or sets the letter spacing (character spacing) for the text.
    /// </summary>
    /// <value>
    /// The letter spacing in pixels. Default is 0.
    /// Positive values increase spacing between characters.
    /// Negative values decrease spacing between characters.
    /// </value>
    public double LetterSpacing
    {
        get => GetValue(LetterSpacingProperty);
        set => SetValue(LetterSpacingProperty, value);
    }
}

AddOwner on TemplatedControl:

/// <summary>
/// Base class for controls that use a template to define their appearance.
/// </summary>
public class TemplatedControl : Control
{
    // ... existing text properties (FontFamily, FontSize, FontWeight, etc.)
    
    /// <summary>
    /// Defines the <see cref="LetterSpacing"/> property.
    /// </summary>
    /// <remarks>
    /// This property uses AddOwner to share the same property definition as 
    /// TextElement.LetterSpacingProperty, following the pattern used by other
    /// text properties like FontSize and FontWeight.
    /// Letter spacing is specified in pixels. Default value is 0 (normal spacing).
    /// This property is inherited by child controls.
    /// </remarks>
    public static readonly StyledProperty<double> LetterSpacingProperty =
        TextElement.LetterSpacingProperty.AddOwner<TemplatedControl>();
    
    /// <summary>
    /// Gets or sets the letter spacing for text displayed by this control.
    /// </summary>
    /// <value>
    /// The letter spacing in pixels. Default is 0.
    /// Positive values increase spacing between characters.
    /// Negative values decrease spacing between characters.
    /// </value>
    public double LetterSpacing
    {
        get => GetValue(LetterSpacingProperty);
        set => SetValue(LetterSpacingProperty, value);
    }
}
  1. Define on TextElement as an inherited attached property (base definition)
  2. AddOwner on TextBlock for direct property access on TextBlock
  3. AddOwner on TemplatedControl for direct property access on all templated controls

Existing examples:

  • FontFamily (TextElement.FontFamilyProperty → TextBlock/TemplatedControl)
  • FontSize (TextElement.FontSizeProperty → TextBlock/TemplatedControl)
  • FontWeight (TextElement.FontWeightProperty → TextBlock/TemplatedControl)
  • FontStyle (TextElement.FontStyleProperty → TextBlock/TemplatedControl)
  • Foreground (TextElement.ForegroundProperty → TextBlock/TemplatedControl)

Usage Examples

Apply directly on controls:

<Button Content="Click" LetterSpacing="2" />
<CheckBox Content="Accept" LetterSpacing="0.5" />

Styles:

<Style Selector="Button.wide">
    <Setter Property="LetterSpacing" Value="3" />
</Style>

Describe alternatives you've considered

No response

Additional context

No response

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions