Why Is Button Foreground Color Ignored in WPF? (Solved)
When designing custom window controls or dark-themed buttons in WPF, a common frustrating issue occurs: you create a custom ControlTemplate, customize the hover and press states, but the button text color (Foreground) refuses to change—even though setting the background works fine.
The Core Problem
In WPF, the Foreground property on controls like Button is not a native property of ContentPresenter. When you provide plain text as the Button.Content, WPF generates a lightweight TextBlock inside the ContentPresenter. That TextBlock inherits the attached property TextElement.Foreground down the visual tree.
There are two primary reasons your text foreground isn't changing as expected:
- Triggers inside ControlTemplate: When using
<Setter Property="Foreground" Value="..." />inside<ControlTemplate.Triggers>without aTargetName, it modifies the template parent, but the visual text elements inside the template often fail to receive property change notifications unless targeted directly usingTextElement.Foreground. - Manual Content Binding on ContentPresenter: Explicitly writing
Content="{TemplateBinding Content}"on aContentPresenterinside aControlTemplatedisables the automatic property aliasing that WPF performs forContentControlelements.
Why Did the Canvas Work?
In your custom UserControl with a Canvas, you used an explicit binding:
Fill="{Binding RelativeSource={RelativeSource AncestorType=Button}, Path=Foreground}"This explicitly searched up the visual tree for the Button instance and fetched its Foreground value directly. However, standard text relies entirely on ambient property inheritance via TextElement.Foreground, which was being intercepted or not propagated by the template.
The Solution
To fix this cleanly and ensure both direct properties (like Foreground="Maroon") and trigger states (like IsMouseOver) work for text and icons, follow these two adjustments:
1. Update ControlTemplate.Triggers to Target TextElement.Foreground
Specify TargetName="contentPresenter" (or target the parent Border) and use TextElement.Foreground instead of raw Foreground in your template triggers.
2. Remove Explicit TemplateBindings from ContentPresenter
Allow the ContentPresenter to automatically inherit its content and properties from the templated Button.
Complete Working Style Example
Here is the corrected and modernized WindowControl button style:
<Style x:Key="WindowControl" TargetType="{x:Type Button}"> <Setter Property="Padding" Value="0,0,0,4"/> <Setter Property="VerticalContentAlignment" Value="Center"/> <Setter Property="HorizontalContentAlignment" Value="Center"/> <Setter Property="Width" Value="45"/> <Setter Property="Height" Value="35"/> <Setter Property="FontSize" Value="13"/> <Setter Property="FontWeight" Value="Normal"/> <Setter Property="Background" Value="{StaticResource Button.Static.Control.Background}"/> <Setter Property="Foreground" Value="{StaticResource Button.Static.Control.Foreground}"/> <Setter Property="BorderThickness" Value="0"/> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type Button}"> <Border x:Name="border" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" SnapsToDevicePixels="True"> <ContentPresenter x:Name="contentPresenter" Focusable="False" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}" Margin="{TemplateBinding Padding}" RecognizesAccessKey="True" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/> </Border> <ControlTemplate.Triggers> <Trigger Property="IsMouseOver" Value="True"> <Setter Property="Background" TargetName="border" Value="{StaticResource Button.MouseOver.Control.Background}"/> <Setter Property="TextElement.Foreground" TargetName="contentPresenter" Value="{StaticResource Button.MouseOver.Control.Foreground}"/> </Trigger> <Trigger Property="IsPressed" Value="True"> <Setter Property="Background" TargetName="border" Value="{StaticResource Button.Pressed.Control.Background}"/> <Setter Property="TextElement.Foreground" TargetName="contentPresenter" Value="{StaticResource Button.Pressed.Control.Foreground}"/> </Trigger> <Trigger Property="IsEnabled" Value="False"> <Setter Property="Background" TargetName="border" Value="{StaticResource Button.Disabled.Control.Background}"/> <Setter Property="TextElement.Foreground" TargetName="contentPresenter" Value="{StaticResource Button.Disabled.Control.Foreground}"/> </Trigger> </ControlTemplate.Triggers> </ControlTemplate> </Setter.Value> </Setter> </Style>Summary
- Use
TextElement.Foregroundtargeting theContentPresenterinControlTemplate.Triggersto update text color properly. - Do not manually declare
Content="{TemplateBinding Content}"on aContentPresenterinside aContentControltemplate, as it disrupts built-in property inheritance.