Why implicit TextBlock style takes over everything

If you redefine implicit TextBlock style in application resources, things don't look good. Resulting style takes over all TextBlocks in the application. People complained about it many times, e.g. on StackOverflow, on StackOverflow again, on MSDN forums, and on Terelic forums.

Suppose we have a button with blue italic text:

<Button Width="200" Foreground="Blue" FontStyle="Italic">Blue Italic Button</Button>

We then go ahead and define an implicit TextBlock style in our application ressources:

<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

Unexpectedly, this style takes over the button: .

The button text is no longer blue, nor it is italic. Button's Foreground and FontStyle properties are ignored. Why is that so?

Firstly, it is important to realize that in our case the button content is of type String. String is not a visual element and cannot be displayed directly. If the string does not contain underscores, ContentPresenter situated inside the button creates a TextBlock to show the string. If the string does contain underscores, it will be represented by an AcessText element instead.

This implicitly created TextBlock is defined simply as

<TextBlock Text="Blue Italic Button" />

The TextBlock does not have any other properties of its own, so its Foreground, FontFamily and other properties must be defined from the environment. Values for WPF dependency properties may come from a variety of sources. These sources are arranged by priority, as outlined in MSDN article "Dependency property value precendence". Here's the list of sources, in order of precedence:

  1. Property system coercion.
  2. Active animations or animations with a Hold behavior.
  3. Local value.
  4. TemplatedParent template properties.
  5. Implicit Style (applies to Style property only).
  6. Style triggers.
  7. Tempalte triggers.
  8. Style setters.
  9. Default (theme) style.
  10. Inheritance.
  11. Default value from property metadata.

When implicit style for TextBlock is not present, its color and other properties are inherited from the parent cotrol, i.e. the Button. "Inheritance" is number 10 on the precedence list. Here's how this looks in Snoop:

When the implicit style is present, the picture changes dramatically. First, the Style property of the TextBlock is set to the implicit style (#5 on the list), because automatically generated TextBlock does not have a style of its own. Then, property setters from that style (#8) will take precendence over the values inherited from the parent (#10). This is why the button's "blue" and "italic" properties are ignored, and the implicit style prevails. Screenshot from Snoop to prove this:

We of course can take matters back into our own hands by redefining the button Content as explicit TextBlock:

<Button Width="200">
    <TextBlock  Foreground="Blue" FontStyle="Italic" Text="Blue Italic Button" />
</Button>

The button now looks like this:

Foreground and FontStyle now come from "local value" (#3 on the precedence list), but anything unspecified locally, such as FontFamily comes from the style (#8).

This is definitely a workaround, but not a real solution. Implicit TextBlock style will affect all buttons and other content controls in our application, and it is not practical to rewrite them all to contain explicit content definition. The best way is to leave implicit TextBlock style alone, and apply a style with desired properties to the top-level windows. The properties will then be inherited by the text blocks in a less dramatic way. See my article WPF: Changing default text style for more details.

Feedback

Questions? Comments?
Drop me a line