WPF: Changing default text style

What is the proper way to change text color, font, or size throughout an application?

The best way I know so far is to create a named style targeting type Control (not TextBlock) and manually apply it to all top-level windows in the application. Properties like FontSize and FontFamily are inherited almost universally. Foreground color is overriden in many controls. You will have to style them separately. This is actually a good thing: depending on your theme you may or may not want your button text to be the same color as regular text.

This is how the results of the styling look like:

 
Before styling   After styling

And this is the code:

<!-- App.xaml -->
<Application.Resources>
    <Style x:Key="RedStyle" TargetType="{x:Type Control}">
        <Setter Property="Foreground" Value="Red" />
        <Setter Property="FontSize" Value="14" />
        <Setter Property="FontFamily" Value="Courier New" />
        <Setter Property="FontStyle" Value="Normal" />
    </Style>
</Application.Resources>

<!-- MainWindow.xaml -->
<Window Style="{StaticResource RedStyle}" ...>
    ...
</Window>

Why overriding implicit TextBlock style is not a good idea

If we put the implicit style in application resources, it works too well. If we put the implicit style in main window resources, it is too weak.

Implicit style in application resources

This style affects all text blocks in the application, except those with their own explicit style. It becomes impossible to change appearance of buttons, labels and other controls if corresponding properties are defined in the TextBlock style. Notice how the "Blue Italic Button" is now neither blue nor italic.

<!-- App.xaml -->
<Application.Resources>
    <!-- Don't do this! -->
    <Style TargetType="{x:Type TextBlock}">
        <Setter Property="Foreground" Value="Red" />
        <Setter Property="FontSize" Value="14" />
        <Setter Property="FontFamily" Value="Courier New" />
        <Setter Property="FontStyle" Value="Normal" />
    </Style>
</Application.Resources>

App.xaml MainWindow.xaml Full code

NB: You may find an advice to override the data template for System.String to cancel the negative effects of an implicit TextBlock style. Overriding data template for String is also not a good idea.

Implicit TextBlock style in Window resources

If we define an implicit TextBlock style in main Window resources, we get too little overriding. The style does not apply to TextBlocks inside data templates. Amazingly, implicit style for Label does affect labels inside data templates.

This is not a bug. It was intentionally designed to protect us from the evils of too much overriding. Implicit styles for controls (Label, Button, CheckBox) penetrate all the way through, but implicit styles for non-controls (TextBLock, Rectangle) stop at data template/ContentPresenter boundaries.

<!-- MainWindow.xaml -->
<Window ...>
    <Window.Resources>
        <Style TargetType="{x:Type TextBlock}">
            <Setter Property="Foreground" Value="Red" />
            <Setter Property="FontSize" Value="14" />
            <Setter Property="FontFamily" Value="Courier New" />
            <Setter Property="FontStyle" Value="Normal" />
        </Style>
    </Window.Resources>
    ...
</Window>

MainWindow.xaml Full code

More Info

Why implicit TextBlock style takes over everything.

Why overriding DataTemplate for System.String is a bad idea.

Conclusion

It may be counter-intuitive, but overriding text traits is better done by tweaking the properties of the parent controls rather than defining a TextBlock style. TextBlocks then inherit the properties from parents. Attempts to define an implicit style for TextBlock lead to either too much, or too little overriding.

Feedback

Questions? Comments?
Drop me a line