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>
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:
- Property system coercion.
- Active animations or animations with a Hold behavior.
- Local value.
- TemplatedParent template properties.
- Implicit Style (applies to
Style
property only). - Style triggers.
- Tempalte triggers.
- Style setters.
- Default (theme) style.
- Inheritance.
- 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
Copyright (c) Ivan Krivyakov. Last updated: November 1, 2014