My problem occurs with WPF in .NET 3.5 SP1 and can be described as follows:
I have a default Style
hitting all TextBlock
elements in my UI. That is how it looks:
<Style TargetType="{x:Type TextBlock}">
<Setter Property="TextTrimming" Value="CharacterEllipsis"/>
<Setter Property="Foreground" Value="Red"/>
</Style>
That works fine for all TextBlock
s. In addition to that I have a Button
style including a ControlTemplate
that looks like this (shortened):
<Style x:Key="MyButtonStyle" TargetType="{x:Type Button}" BasedOn="{x:Null}">
<Setter Property="Foreground" Value="Green"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border x:Name="Border"
Background="{TemplateBinding Background}"
BorderThickness="{TemplateBinding BorderThickness}"
Padding="{TemplateBinding Padding}"
Height="24"
BorderBrush="{TemplateBinding BorderBrush}">
<ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
TextBlock.Foreground="{TemplateBinding Foreground}"/>
</Border>
<ControlTemplate.Triggers>...</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Notice the line TextBlock.Foreground="{TemplateBinding Foreground}"
in the ContentPresenter
. This should set the button text to green and in fact it does in the designer view of Visual Studio. But when I compile and run the program the button text is red, the text color is set by the default TextBlock
style. I verified this with Snoop.
How can I prevent the defaultTextBlock
style from overriding the TextBlock.Foreground
value? The OverridesDefaultStyle
property of ContentPresenter
doesn't help in this case.
Any idea?
See answer 5 at this link
This happends because the
ContentPresenter creates a TextBlock
for a string content, and since that
TextBlock isn't in the visual tree, it
will lookup to Application level
resource. And if you define a style
for the TextBlock at Application
level, then it will be applied to
these TextBlock within ContentControl
A workaround is to define a
DataTemplate for System.String, where
we can explicitly use a default
TextBlock to display the content. You
can place that DataTemplate in the
same dictionary you define the
TextBlock style so that this
DataTemplate will be applied to
whatever ContentPresenter effected by
your style.
Try adding this to the ResourceDictionary
<DataTemplate DataType="{x:Type sys:String}">
<TextBlock Text="{Binding}">
<TextBlock.Resources>
<Style TargetType="{x:Type TextBlock}"/>
</TextBlock.Resources>
</TextBlock>
</DataTemplate>
You are better off not overriding default style for the TextBlock. The best idea I could come up with so far is to create a style for Control and apply it to all top level windows.
<!-- App.xaml -->
<Application.Resources>
<Style x:Key="RedStyle" TargetType="{x:Type Control}">
<Setter Property="TextTrimming" Value="CharacterEllipsis"/>
<Setter Property="Foreground" Value="Red"/>
</Style>
</Application.Resources>
<!-- MainWindow.xaml -->
<Window Style="{StaticResource RedStyle}" ...>
...
</Window>
See here for more details: http://www.ikriv.com/dev/dotnet/wpftextstyle/
With respect to the 2 options:
- @Fredrik Hedblad
Try adding this to the ResourceDictionary
<DataTemplate DataType="{x:Type sys:String}">
<TextBlock Text="{Binding}">
<TextBlock.Resources>
<Style TargetType="{x:Type TextBlock}"/>
</TextBlock.Resources>
</TextBlock>
</DataTemplate>
- @Ivan Krivyakov
You are better off not overriding default style for the TextBlock. The best idea I could come up with so far is to create a style for Control and apply it to all top level windows.
I would suggest an alternative approach and use an attached Dependency Property e.g.
namespace AttachedProperties
{
public static class TextBlockExtensions
{
public static bool GetUseAppThemeStyle(DependencyObject obj)
{
return (bool)obj.GetValue(UseAppThemeStyleProperty);
}
public static void SetUseAppThemeStyle(DependencyObject obj, bool value)
{
obj.SetValue(UseAppThemeStyleProperty, value);
}
// Using a DependencyProperty as the backing store for UseAppThemeStyle. This enables animation, styling, binding, etc...
public static readonly DependencyProperty UseAppThemeStyleProperty =
DependencyProperty.RegisterAttached("UseAppThemeStyle", typeof(bool), typeof(TextBlockExtensions), new FrameworkPropertyMetadata(true, FrameworkPropertyMetadataOptions.Inherits));
}
}
NOTE: You can set it to be true or false by default
Then having the namespace:
xmlns:attachedProperties="clr-namespace:AttachedProperties"
make a default style:
<Style TargetType="{x:Type TextBlock}" BasedOn="{StaticResource {x:Type TextBlock}}">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=(attachedProperties:TextBlockExtensions.UseAppThemeStyle), RelativeSource={RelativeSource Mode=Self}}" Value="True">
<Setter Property="TextTrimming" Value="CharacterEllipsis"/>
<Setter Property="Foreground" Value="Red"/>
</DataTrigger>
</Style.Triggers>
</Style>
Then if you need to get the default you can just set the attached property either in a style:
<Style x:Key="blueButtonStyle" TargetType="{x:Type Button}" BasedOn="{StaticResource {x:Type Button}}">
<Setter Property="attachedProperties:TextBlockExtensions.UseAppThemeStyle" Value="False" />
<Setter Property="Foreground" Value="Blue" />
</Style>
or directly on a button:
<Button attachedProperties:TextBlockExtensions.UseAppThemeStyle="False" Foreground="Blue">I'm blue da ba dee da ba die...</Button>