Repository navigation
Disabled Appearance="Primary" button renders white text on light background (invisible) since 4.2.1 #1699
Description
Describe the bug
In WPF-UI 4.2.1, a ui:Button with Appearance="Primary" and IsEnabled="False" keeps the white "on accent" foreground from the enabled-Primary state, leaving the disabled label invisible on the light disabled background. The same XAML in 4.2.0 correctly renders the disabled label in grey.
The regression appears to come from PR #1680 ("Proper foreground on Button content"), which moved the Disabled foreground setter from per-element (TargetName="ContentPresenter" / TargetName="ControlIcon" with TextElement.Foreground) to a single Style-level <Setter Property="Foreground" .../>. Because the Appearance="Primary" trigger is declared after the IsEnabled="False" trigger in Button.xaml and also sets Foreground (to AccentButtonForeground), it wins for disabled Primary buttons.
To Reproduce
Repro
Minimal, isolated reproduction — only <PackageReference Include="WPF-UI" />, the stock ThemesDictionary + ControlsDictionary, ApplicationThemeManager.Apply(ApplicationTheme.Light), and a bare ui:FluentWindow. No additional resources or styles.
App.xaml
<Application xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:ui="http://schemas.lepo.co/wpfui/2022/xaml"
StartupUri="MainWindow.xaml">
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ui:ThemesDictionary Theme="Light" />
<ui:ControlsDictionary />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
</Application>App.xaml.cs
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
ApplicationThemeManager.Apply(ApplicationTheme.Light);
}MainWindow.xaml
<ui:FluentWindow xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:ui="http://schemas.lepo.co/wpfui/2022/xaml"
x:Class="WpfUiButtonRepro.MainWindow"
Width="720" Height="280">
<StackPanel Margin="24">
<TextBlock Text="Enabled" />
<WrapPanel>
<ui:Button Appearance="Primary" Content="Primary" Margin="4" />
<ui:Button Appearance="Secondary" Content="Secondary" Margin="4" />
<ui:Button Appearance="Success" Content="Success" Margin="4" />
<ui:Button Appearance="Caution" Content="Caution" Margin="4" />
<ui:Button Appearance="Danger" Content="Danger" Margin="4" />
</WrapPanel>
<TextBlock Text="Disabled" />
<WrapPanel>
<ui:Button Appearance="Primary" Content="Primary" IsEnabled="False" Margin="4" />
<ui:Button Appearance="Secondary" Content="Secondary" IsEnabled="False" Margin="4" />
<ui:Button Appearance="Success" Content="Success" IsEnabled="False" Margin="4" />
<ui:Button Appearance="Caution" Content="Caution" IsEnabled="False" Margin="4" />
<ui:Button Appearance="Danger" Content="Danger" IsEnabled="False" Margin="4" />
</WrapPanel>
</StackPanel>
</ui:FluentWindow>Actual
In 4.2.1 the disabled Primary button keeps Foreground = AccentButtonForeground (white), so the label is invisible against the light disabled background.
| 4.2.0 (correct) | 4.2.1 (regression) |
|---|---|
Suspected cause
In src/Wpf.Ui/Controls/Button/Button.xaml:
- 4.2.0 disabled trigger applied the foreground per-element:
<Setter TargetName="ControlIcon" Property="TextElement.Foreground" Value="{DynamicResource ButtonForegroundDisabled}" /> <Setter TargetName="ContentPresenter" Property="TextElement.Foreground" Value="{DynamicResource ButtonForegroundDisabled}" />
- 4.2.1 disabled trigger sets it at the Style level:
<Setter Property="Foreground" Value="{DynamicResource ButtonForegroundDisabled}" />
The later Trigger Property="Appearance" Value="Primary" also sets Foreground="{DynamicResource AccentButtonForeground}", and Style-level triggers are evaluated in declaration order (later wins), so the disabled foreground no longer takes effect for Primary buttons.
Workaround
A BasedOn style with a MultiTrigger placed in window-scoped resources (does not work at App level for the usual implicit-style + BasedOn={x:Type ui:Button} lookup-order reasons):
<ui:FluentWindow.Resources>
<Style TargetType="ui:Button" BasedOn="{StaticResource {x:Type ui:Button}}">
<Style.Triggers>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsEnabled" Value="False" />
<Condition Property="Appearance" Value="Primary" />
</MultiTrigger.Conditions>
<Setter Property="Foreground" Value="{DynamicResource ButtonForegroundDisabled}" />
</MultiTrigger>
</Style.Triggers>
</Style>
</ui:FluentWindow.Resources>Suggested fix
Either restore the per-element TargetName=... setters in the Disabled trigger, or move the Appearance="Primary" trigger above the IsEnabled="False" trigger so the disabled Style setter wins.
Environment
- WPF-UI 4.2.1 (regression) vs 4.2.0 (correct)
- .NET 8 (
net8.0-windows) - Windows 11 Pro
- Theme:
ApplicationTheme.Light(also reproduces withDark) ui:FluentWindowhost,ApplicationThemeManager.Apply(...)at startup, no other resources merged
Expected behavior
Expected
The disabled Appearance="Primary" button should render the "Primary" label in the disabled-foreground colour (grey), as it does in 4.2.0 and as the other appearances (Secondary/Success/Caution/Danger) still do in 4.2.1.
Screenshots
OS version
Windows 11 Pro
.NET version
.NET 8 (net8.0-windows)
WPF-UI NuGet version
WPF-UI 4.2.1
Additional context
No response
Activity
pomianowski commentedon Apr 30, 2026
Possibly related #1680, @dkuaf