Fixing Broken DefaultButtonStyle in Windows UI (WinUI) / WinAppSdk

In writing my previous post on Implicit and Explicit Styles in XAML I was reminded of a bug in the definition of the DefaultButtonStyle which prevents you setting the Foreground colour of a Button. The issue is documented, along with the fix, in this issue but for whatever reason (and I can’t think of one that makes sense) Microsoft has ignored it for 5 service releases (1.1.1, 1.1.2, 1.1.3, 1.1.4 and 1.1.5). I’m going to provide an updated DefaultButtonStyle which you can use in your application that fixes the issue – it’s a SINGLE missing attribute on the ContentPresenter.

Firstly, here’s the issue – you want to change the Foreground of a Button

<Button Foreground="Pink" />

This seems to work as the Foreground (ie the text) of the Button is updated

However, if you hover over the Button, the colour changes (correct behaviour due to the VisualStates on the Button) but then the colour stays changed when you move the mouse away (incorrect behaviour). This is because the Foreground property isn’t being set on the ContentPresenter (it’s being inherited as Foreground is an ambient property) which means that the VisualStateManager doesn’t know what value to return it to – you could argue this is also a bug in the VisualStateManager.

Anyhow, here’s the fix – you can copy and paste this into your App.xaml and it will replace the existing DefaultButtonStyle defined by WinUI. The single additional attribute is bolded so you can see what a small change it is.

<Style x:Key="DefaultButtonStyle"
        TargetType="Button">
    <Setter Property="Background"
            Value="{ThemeResource ButtonBackground}" />
    <Setter Property="BackgroundSizing"
            Value="InnerBorderEdge" />
    <Setter Property="Foreground"
            Value="{ThemeResource ButtonForeground}" />
    <Setter Property="BorderBrush"
            Value="{ThemeResource ButtonBorderBrush}" />
    <Setter Property="BorderThickness"
            Value="{ThemeResource ButtonBorderThemeThickness}" />
    <Setter Property="Padding"
            Value="{StaticResource ButtonPadding}" />
    <Setter Property="HorizontalAlignment"
            Value="Left" />
    <Setter Property="VerticalAlignment"
            Value="Center" />
    <Setter Property="FontFamily"
            Value="{ThemeResource ContentControlThemeFontFamily}" />
    <Setter Property="FontWeight"
            Value="Normal" />
    <Setter Property="FontSize"
            Value="{ThemeResource ControlContentThemeFontSize}" />
    <Setter Property="UseSystemFocusVisuals"
            Value="{StaticResource UseSystemFocusVisuals}" />
    <Setter Property="FocusVisualMargin"
            Value="-3" />
    <Setter Property="CornerRadius"
            Value="{ThemeResource ControlCornerRadius}" />
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="Button">
                <ContentPresenter x:Name="ContentPresenter"
                                    Foreground="{TemplateBinding Foreground}"
                                    Background="{TemplateBinding Background}"
                                    BackgroundSizing="{TemplateBinding BackgroundSizing}"
                                    BorderBrush="{TemplateBinding BorderBrush}"
                                    BorderThickness="{TemplateBinding BorderThickness}"
                                    Content="{TemplateBinding Content}"
                                    ContentTemplate="{TemplateBinding ContentTemplate}"
                                    ContentTransitions="{TemplateBinding ContentTransitions}"
                                    CornerRadius="{TemplateBinding CornerRadius}"
                                    Padding="{TemplateBinding Padding}"
                                    HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
                                    VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"
                                    AutomationProperties.AccessibilityView="Raw"
                                    local:AnimatedIcon.State="Normal"
                                    xmlns:local="using:Microsoft.UI.Xaml.Controls">
                    <ContentPresenter.BackgroundTransition>
                        <BrushTransition Duration="0:0:0.083" />
                    </ContentPresenter.BackgroundTransition>

                    <VisualStateManager.VisualStateGroups>
                        <VisualStateGroup x:Name="CommonStates">
                            <VisualState x:Name="Normal" />

                            <VisualState x:Name="PointerOver">

                                <Storyboard>
                                    <ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter"
                                                                    Storyboard.TargetProperty="Background">
                                        <DiscreteObjectKeyFrame KeyTime="0"
                                                                Value="{ThemeResource ButtonBackgroundPointerOver}" />
                                    </ObjectAnimationUsingKeyFrames>
                                    <ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter"
                                                                    Storyboard.TargetProperty="BorderBrush">
                                        <DiscreteObjectKeyFrame KeyTime="0"
                                                                Value="{ThemeResource ButtonBorderBrushPointerOver}" />
                                    </ObjectAnimationUsingKeyFrames>
                                    <ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter"
                                                                    Storyboard.TargetProperty="Foreground">
                                        <DiscreteObjectKeyFrame KeyTime="0"
                                                                Value="{ThemeResource ButtonForegroundPointerOver}" />
                                    </ObjectAnimationUsingKeyFrames>
                                </Storyboard>
                                <VisualState.Setters>
                                    <Setter Target="ContentPresenter.(controls:AnimatedIcon.State)"
                                            Value="PointerOver" />

                                </VisualState.Setters>
                            </VisualState>

                            <VisualState x:Name="Pressed">

                                <Storyboard>
                                    <ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter"
                                                                    Storyboard.TargetProperty="Background">
                                        <DiscreteObjectKeyFrame KeyTime="0"
                                                                Value="{ThemeResource ButtonBackgroundPressed}" />
                                    </ObjectAnimationUsingKeyFrames>
                                    <ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter"
                                                                    Storyboard.TargetProperty="BorderBrush">
                                        <DiscreteObjectKeyFrame KeyTime="0"
                                                                Value="{ThemeResource ButtonBorderBrushPressed}" />
                                    </ObjectAnimationUsingKeyFrames>
                                    <ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter"
                                                                    Storyboard.TargetProperty="Foreground">
                                        <DiscreteObjectKeyFrame KeyTime="0"
                                                                Value="{ThemeResource ButtonForegroundPressed}" />
                                    </ObjectAnimationUsingKeyFrames>
                                </Storyboard>
                                <VisualState.Setters>
                                    <Setter Target="ContentPresenter.(controls:AnimatedIcon.State)"
                                            Value="Pressed" />

                                </VisualState.Setters>
                            </VisualState>

                            <VisualState x:Name="Disabled">

                                <Storyboard>
                                    <ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter"
                                                                    Storyboard.TargetProperty="Background">
                                        <DiscreteObjectKeyFrame KeyTime="0"
                                                                Value="{ThemeResource ButtonBackgroundDisabled}" />
                                    </ObjectAnimationUsingKeyFrames>
                                    <ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter"
                                                                    Storyboard.TargetProperty="BorderBrush">
                                        <DiscreteObjectKeyFrame KeyTime="0"
                                                                Value="{ThemeResource ButtonBorderBrushDisabled}" />
                                    </ObjectAnimationUsingKeyFrames>
                                    <ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter"
                                                                    Storyboard.TargetProperty="Foreground">
                                        <DiscreteObjectKeyFrame KeyTime="0"
                                                                Value="{ThemeResource ButtonForegroundDisabled}" />
                                    </ObjectAnimationUsingKeyFrames>
                                </Storyboard>
                                <VisualState.Setters>
                                    <!-- DisabledVisual Should be handled by the control, not the animated icon. -->
                                    <Setter Target="ContentPresenter.(controls:AnimatedIcon.State)"
                                            Value="Normal" />

                                </VisualState.Setters>
                            </VisualState>

                        </VisualStateGroup>

                    </VisualStateManager.VisualStateGroups>
                </ContentPresenter>

            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

Note that if you want the updated DefaultButtonStyle to be the new implicit Style for Button, you’ll also need to add:

<!-- Default style for Windows.UI.Xaml.Controls.Button -->
<Style TargetType="Button" BasedOn="{StaticResource DefaultButtonStyle}" />

Hope this helps anyone frustrated by the issue