Skip to content

fix(controls): Fix TextBlock style, typography and related issues#1631

Closed
apachezy wants to merge 3 commits intolepoco:mainfrom
apachezy:fix/textblock
Closed

fix(controls): Fix TextBlock style, typography and related issues#1631
apachezy wants to merge 3 commits intolepoco:mainfrom
apachezy:fix/textblock

Conversation

@apachezy
Copy link
Copy Markdown
Contributor

@apachezy apachezy commented Jan 2, 2026

Pull request type

Please check the type of change your PR introduces:

  • Update
  • Bugfix
  • Feature
  • Code style update (formatting, renaming)
  • Refactoring (no functional changes, no api changes)
  • Build related changes
  • Documentation content changes

What is the current behavior?

The current TextBlock implementation suffers from several design flaws and functional issues:

1. Implicit Style System Broken

  • Incorrect Constructor Logic: PR (Fix Wpf.Ui.Controls.TextBlock default style #1347) forcibly applied SetResourceReference(StyleProperty, defaultFontTypography.ToResourceValue()) in the constructor, setting a high-priority style reference that completely disabled implicit styling (including locally defined implicit styles).

  • Improper Style Resource Definitions: The FontTypography resources predefined in Typography.xaml were typed as Style. When applied to TextBlock, they would replace the entire control's style—not just typography properties—causing the loss of foreground, background, alignment, and other style attributes (including the control template).

2. Confused Property Priority

  • FontTypography vs. Native Property Conflicts: When both FontTypography and native properties like FontSize or FontWeight were set together, the order of effect depended on the declaration order in XAML, rather than the intended design logic.

    Example:

    <ui:TextBlock FontSize="36" FontTypography="Subtitle" FontWeight="Normal" />
    • FontSize="36" applied first

    • FontTypography="Subtitle" overrode the font size to 20

    • FontWeight="Normal" then overrode Subtitle's SemiBold

    • Final result: FontSize=20, FontWeight=Normal—contrary to expectation.

3. Design Pattern Inconsistencies with WPF Conventions

  • Enums as Property Type: The FontTypography and Appearance properties used enums instead of static resource classes (like WPF’s FontWeights, Brushes), requiring a resource lookup on every property change and incurring unnecessary performance overhead.

  • Vertical Alignment Issues: Issue The title is not aligned. #1617 and the closed PR fix(controls): Resolve vertical alignment issue in WPFUI TextBlock #1624 highlighted abnormal vertical alignment in TextBlock.

  • Redundant Style Settings: TextBlock.xaml contained redundant and harmful style properties that interfered with the control's normal behavior.

Issue Number: #920 #1617 #1459 #1248 #887 #908 #890

What is the new behavior?

This fix refactors the TextBlock implementation, preserving WPF’s native characteristics while ensuring WPFUI-specific features work correctly.

1. Restore WPF Native Styling System

  • Remove Destructive Code: Delete the SetResourceReference call in the constructor and any logic that bypassed style priority rules.

  • Clean Up Redundant Styles: Simplify TextBlock.xaml, keeping only necessary property settings, so that native TextBlock behaves correctly while retaining WPFUI’s theme styling.

2. Redesign the Typography System

  • Introduce FontTypographyPreset Class: A lightweight typography preset class containing properties like FontSize and FontWeight, replacing full Style resources.

  • Refactor Resource Definitions: Update Typography.xaml to define typography presets using FontTypographyPreset and adjust resource key names for clarity.

    Preset typography Style resources are retained for direct use; however, Appearance properties still take precedence.
    e.g. <ui:TextBlock Text="Subtitle" Style="{StaticResource SubtitleTextBlockStyle}" />.

  • Update Enum Mapping: Adjust the enum mapping in TextBlockFontTypographyExtensions to align with the new resource definitions.

3. Clear Property Priority Rules

  • Nullable-Type Design: Change FontTypography and Appearance to nullable types (Nullable) to clearly distinguish between “set” and “unset” states.

  • Priority Logic:

    • When FontTypography or Appearance is set and its internal property (e.g., FontSize) is not null, use the preset value.

    • When these properties are unset or their internal property is null, fall back to the native property value.

    • Preset properties have higher priority than native properties, but only when explicitly set.

4. Native TextBlock Control Compatibility

This represents a design trade-off: while native WPF controls should, in principle, retain their default behavior, one of WPFUI’s design goals is to allow native controls to adopt the Fluent design style.

To achieve this, the following mechanisms have been introduced:

  • TextBlockMetadataInitializer: Overrides the dependency property metadata of the native TextBlock during library initialization, enabling it to use WPFUI-defined defaults (such as font size and theme‑responsive foreground colors) while still allowing further customization at the application level.

  • TextBlockHelper: Provides a default foreground color for native TextBlock via an attached property. When the foreground is not explicitly set, the current theme’s TextFillColorPrimaryBrush is automatically applied, ensuring text colors remain consistent with the Fluent theme.

  • Automatic Initialization Integration: Calls TextBlockMetadataInitializer.EnsureInitialized() in the ControlsDictionary constructor, guaranteeing that when this resource dictionary is referenced by the application (typically in App.xaml), the native TextBlock metadata is overridden promptly, without requiring extra initialization by developers.

This design maintains compatibility with native controls while providing a smooth migration path for scenarios that wish to apply the Fluent style globally.

5. Fix Known Issues

  • Fixed Remove all FontSize setting from the global style ? #920

    • Removed the forcibly set font sizes in TextBlock.xaml, which were breaking WPF’s inheritance mechanism (e.g., when a parent specifies a FontSize property, children should inherit that value). If a default value for TextBlock is needed, the correct approach is to modify the property’s default value in its metadata, not to enforce a “default” through style setters.
  • Fixed The title is not aligned. #1617

    • Removed the LineHeight property: Resolves the vertical-center alignment issues caused by explicitly setting LineHeight.

      Additional note: Investigation shows that while the WinUI Gallery typography page lists FontSize/LineHeight ratios, the WinUI implementation itself does not explicitly apply the LineHeight property. These ratios appear to serve as design references rather than values intended to be enforced in code.

  • Fixed In WPF-UI version 4.0.3, it is no longer possible to override global control styles (e.g., TextBlock.FontSize) from App.xaml using implicit styles #1459

    • Restored implicit style support: Implicit styles can now be used from App.xaml or other resource levels to override global control styles (e.g., TextBlock.FontSize). Previously this was disabled due to the forced style reference in the constructor.
  • Fixed Font size identical for Body and Caption FontTypographies #1248

    • Fixed property‑application logic: Redesigned the change‑handling mechanism for the FontTypography property, ensuring that when different enum values (such as Body and Title) are set, the corresponding typography preset is correctly applied, fixing the previous logic flaw that prevented proper font‑size differentiation.
  • Related Style‑System Issues (Inheritance & Priority Fixes)

    These issues all stemmed from violations of WPF’s inheritance mechanism or dependency‑property priority rules and have been uniformly resolved in this refactoring. These issues will be automatically closed when this PR is merged.

6. Backward Compatibility

  • API Unchanged: The public interface of FontTypography and Appearance properties remains the same.

    Upgraded the Appearance and FontTypography properties: Re-registered them as attached properties, allowing them to be applied to native TextBlock controls as well. For example:

    <!-- Native TextBlock -->
    <TextBlock Text="Hello, world!" ui:TextBlock.Appearance="Primary" />
    <TextBlock Text="Hello, world!" ui:TextBlock.FontTypography="Body" />
    
    <!-- Wpf.Ui TextBlock -->
    <ui:TextBlock Text="Hello, world!" FontTypography="Body" />
    <ui:TextBlock Text="Hello, world!" Appearance="Primary" />
  • Default Behavior Preserved: The native TextBlock default style still matches the WPFUI‑predefined “Body” font size and correctly applies WPFUI theme colors.

  • Performance Improvements: Reduced unnecessary resource lookups, improving performance during property changes.

Other information

This fix restores TextBlock’s compliance with WPF’s dependency‑property priority system, resolving core problems such as implicit‑style failure, property conflicts, and vertical‑alignment issues. The new implementation retains WPFUI’s design identity while ensuring consistency with native WPF development patterns, making the control more stable and easier to use.

@apachezy apachezy requested a review from pomianowski as a code owner January 2, 2026 01:27
@github-actions github-actions bot added controls Changes to the appearance or logic of custom controls. styles Topic is related to styles PR Pull request dotnet release labels Jan 2, 2026
@apachezy
Copy link
Copy Markdown
Contributor Author

apachezy commented Jan 2, 2026

This PR has uncovered a new issue: when a TextBlock does not have its FontSize explicitly set (via style, inheritance, local value, etc.), its appearance will revert to the original default size in the designer or when inspected at runtime with a tool like Snoop—even though the current FontSize property value does not change back (the property inspector shows a normal value, and its source is reported as "Default").

I suspect that property scanning or reading certain information incorrectly triggers a re‑layout. This behavior may be related to a flaw in the dependency‑property system’s handling of default‑value “application” or “coercion” logic under specific triggering conditions (such as property queries, layout passes, or visual‑tree state updates) for inherited controls.

I’m converting this PR to draft. Please do not merge it until the root cause is understood and resolved.

@apachezy apachezy marked this pull request as draft January 2, 2026 11:15
@apachezy
Copy link
Copy Markdown
Contributor Author

apachezy commented Jan 3, 2026

Resolved — reopening this PR.

The reason this PR was previously marked as Draft was a layout size fluctuation observed at design time. After further investigation, this issue was identified as originating from a behavioral issue in the native WPF TextBlock.

When certain rich-text-related properties are accessed (such as ContentStart and ContentEnd, which commonly happens when designers or property inspectors enumerate properties), TextBlock may be unconditionally forced into ComplexContent mode.
In this mode, text is represented by Run/Inline elements, which, if they do not inherit an effective FontSize, fall back to the default FontSize defined on TextElement.

This becomes observable under the following combination of conditions:

  • The TextBlock.FontSizeProperty metadata is overridden to change its default value
  • FontSize is not explicitly set on the control
  • Design-time interactions (such as selecting the control or property inspection) trigger the ComplexContent path

The result is a visible issue where the font size appears to “revert” to a smaller value when the control is inspected or selected at design time.

The resolution is based on two key observations:

  • In the WPF Fluent theme, the default FontSize for TextElement is 14, whereas the classic WPF default is 12
  • WPFUI is designed specifically to provide Fluent-styled controls and theming

Therefore, this fix aligns the default FontSize of TextElement with the Fluent design by setting it to 14.
This change is semantically consistent within the Fluent context and naturally eliminates the design-time issue described above, preventing inheritance breakage, implicit style inconsistencies, and font size fluctuations.

Based on this, the PR has been restored to an open, reviewable state.

@apachezy apachezy marked this pull request as ready for review January 3, 2026 17:06
@apachezy apachezy force-pushed the fix/textblock branch 2 times, most recently from 389fb07 to 933964f Compare January 4, 2026 13:13
@apachezy apachezy marked this pull request as draft January 4, 2026 16:34
@apachezy apachezy marked this pull request as ready for review January 5, 2026 01:18
@apachezy apachezy force-pushed the fix/textblock branch 4 times, most recently from 1b073fa to e7a0fbe Compare January 5, 2026 20:07
@apachezy
Copy link
Copy Markdown
Contributor Author

@pomianowski

This comment is directed to you as the repository owner.

I want to provide some additional context on the design choices and implementation details of this PR for your awareness:

  1. TextBlockTheming & Attached Properties

    • These are intentionally designed to avoid inflating ui:TextBlock and to provide “fallback” and theme-aware behavior.
    • They allow features to be shared with native TextBlock via attached properties without breaking high-frequency, lightweight controls.
  2. FontTypographyProperty & Presets

    • The FontTypographyProperty is an enum, mapping to resource keys rather than values.
    • Using presets is necessary to correctly resolve FontSize and FontWeight without breaking inheritance.
    • Predefined style resources exist for convenience, but the PR ensures the core logic works independently of them.
  3. FallbackForeground

    • Provides a late-bound, theme-aware default foreground without relying on hardcoded metadata or style setters.
    • Ensures TextBlock always respects the library’s dynamic theming while minimizing runtime overhead.
  4. Design Flexibility

    • The current setup allows minimal coupling, leaving room for future extensions without forcing changes to ui:TextBlock.
    • Either TextBlockTheming or ui:TextBlock could be removed independently while preserving core behavior.

The PR aims to address multiple issues while maintaining WPF best practices, including dependency property inheritance, dynamic theming, and high-frequency control performance.

I respect the review process and understand decisions are ultimately up to the assigned reviewer, but I wanted to ensure the rationale and constraints behind this design are clear for your awareness.

@apachezy
Copy link
Copy Markdown
Contributor Author

@pomianowski,

I’d like to add a short clarification in case the intent of this PR is not fully clear, especially regarding the discussion around Styles vs presets.

1. Preset vs. “FontTypographyStyle” (style-based approach)

If we assume an alternative design where an attached property like FontTypographyStyle is introduced, it effectively leads to one of two behaviors:

a) Read values from a Style and apply them to the TextBlock

  • This becomes a comparison between:
    • preset.FontSize / preset.FontWeight
    • vs. enumerating setters, e.g.
      style.Setters.OfType<Setter>().FirstOrDefault(s => s.Property == TextBlock.FontSizeProperty)?.Value
  • In this case, the difference in complexity, clarity, and determinism is quite obvious.

b) Apply the Style directly to the TextBlock

  • This is functionally equivalent to:
    <TextBlock Style="{StaticResource SomeTypographyStyle}" />
  • Which does not provide any additional abstraction over directly using styles.

This is why FontTypographyPreset is not intended to replace styles, but to provide a lightweight, explicit, and deterministic mapping for typography-related values only.


2. TryFindResource vs. proxy (attached) properties

Regarding the discussion about TryFindResource versus proxy properties:

  • TryFindResource performs an explicit, tree-walking lookup.
  • A proxy attached property participates in the dependency property system and leverages mechanisms similar to DynamicResource, including cached expressions and internal indexing.
  • Only when those mechanisms cannot resolve the value does a resource lookup occur.

This distinction is important in the context of TextBlock, which is a high-frequency, lightweight control.


3. Foreground resolution

The foreground resolution logic is intentionally designed to balance theme safety, inheritance behavior, and predictable fallbacks.

While this results in a multi-step pipeline, the goal is to ensure correctness under dynamic theming rather than to introduce additional complexity.

If a simpler approach is preferred, this can be revisited separately.


4. Overall design intent

More broadly, the design choices in this PR are guided by a few core principles:

  • Avoid coupling runtime behavior to Style structure, especially for properties with inheritance semantics.
  • Prefer deterministic, explicit resolution paths over implicit behavior derived from style inspection.
  • Keep high-frequency controls like TextBlock lightweight and predictable under dynamic theming.
  • Separate rendering concerns from semantic or fallback-related logic, even if that introduces additional supporting types.

These decisions may trade some surface-level convenience for clarity, correctness, and long-term maintainability.

If you feel that this design is overly complex, too indirect, or simply not aligned with the direction of the library, I fully understand and accept that judgment. My intent here is to clearly document the reasoning behind the approach, even if a different trade-off is ultimately preferred.

Thank you for taking the time to read this clarification.

@apachezy
Copy link
Copy Markdown
Contributor Author

唉,即使经过AI工具和翻译器我也很难表达出我的设计意图和理解讨论内容。
我在这里用中文留个言,不知各位是否能看懂?

@Nuklon Nuklon mentioned this pull request Jan 11, 2026
7 tasks
@Nuklon
Copy link
Copy Markdown
Collaborator

Nuklon commented Jan 11, 2026

Closing in favor of #1640

@Nuklon Nuklon closed this Jan 11, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

controls Changes to the appearance or logic of custom controls. dotnet PR Pull request release styles Topic is related to styles

Projects

None yet

2 participants