-1

I'm currently styling a button template in WPF .NET Core 3.1 in a resource dictionary and I want to fade the button color when the button's IsEnabled property is set to true/false. According to this link it appears we can easily do this with Trigger.Enter/ExitActions. However, I can't seem to get it to work.

I've tried a few variations:

<Trigger Property="IsEnabled" Value="True">
   <Trigger.EnterActions>
       <BeginStoryboard>
           <Storyboard>
              <ColorAnimation Storyboard.TargetName="border"
                              Storyboard.TargetProperty="Background.Color"
                              Duration="0:0:0.3"
                              To="{StaticResource ButtonGray}"/>
           </Storyboard>
       </BeginStoryboard>
   </Trigger.EnterActions>
</Trigger>

Putting it all in one trigger:

<Trigger Property="IsEnabled" Value="false">
   <Setter Property="Background" TargetName="border" Value="{StaticResource Button.Disabled.BackgroundBrush}"/>
   <Setter Property="BorderBrush" TargetName="border" Value="{StaticResource Button.Disabled.BorderBrush}"/>
   <Setter Property="TextElement.Foreground" TargetName="contentPresenter" Value="{StaticResource Button.Disabled.ForegroundBrush}"/>
   <Trigger.ExitActions>
      <BeginStoryboard>
          <Storyboard>
              <ColorAnimation Storyboard.TargetName="border"
                              Storyboard.TargetProperty="Background.Color"
                              Duration="0:0:0.3"
                              To="{StaticResource ButtonGray}"/>
           </Storyboard>
      </BeginStoryboard>
  </Trigger.ExitActions>
</Trigger>

Finally, I tried where the trigger has both EnterActions and ExitActions in the same trigger. Any idea why this isn't working?

UPDATE: Whole Button Style:

<Style TargetType="{x:Type Control}" x:Key="ControlBase">
    <Setter Property="FontFamily" Value="Poppins"/>
</Style>

<!-- Gray Button Styles -->
<Color x:Key="SmallButtonGray">#BB222222</Color>
<SolidColorBrush x:Key="SmallButtonGrayBrush" Color="{StaticResource SmallButtonGray}"/>

<Color x:Key="Button.Hover.BGGray">#BB333333</Color>
<SolidColorBrush x:Key="Button.Hover.BGGrayBrush" Color="{StaticResource Button.Hover.BGGray}"/>

<Color x:Key="Button.Click.Gray">#BB888888</Color>
<SolidColorBrush x:Key="Button.Click.GrayBrush" Color="{StaticResource Button.Click.Gray}"/>

<Color x:Key="Button.Disabled.Background">#44BBBBBB</Color>
<SolidColorBrush x:Key="Button.Disabled.BackgroundBrush" Color="{StaticResource Button.Disabled.Background}"/>

<Color x:Key="Button.Disabled.Border">#FFADB2B5</Color>
<SolidColorBrush x:Key="Button.Disabled.BorderBrush" Color="{StaticResource Button.Disabled.Border}"/>

<Color x:Key="Button.Disabled.Foreground">#FF838383</Color>
<SolidColorBrush x:Key="Button.Disabled.ForegroundBrush" Color="{StaticResource Button.Disabled.Foreground}"/>

<!-- Small Button Styles -->
<Style x:Key="SmallButtonStyle" TargetType="Button" BasedOn="{StaticResource ControlBase}">
    <Setter Property="SnapsToDevicePixels" Value="True"/>
    <Setter Property="FontWeight" Value="Light" />
    <Setter Property="FontSize" Value="18"/>
    <Setter Property="TextOptions.TextFormattingMode" Value="Display" />
    <Setter Property="TextOptions.TextRenderingMode" Value="ClearType"/>
    <Setter Property="UseLayoutRounding" Value="True"/>
    <Setter Property="BorderThickness" Value="1"/>
</Style>

<!-- Page Buttons-->
<Style x:Key="GrayButtonStyle" TargetType="Button" BasedOn="{StaticResource SmallButtonStyle}">
    <Setter Property="Background" Value="{StaticResource SmallButtonGrayBrush}"/>
    <Setter Property="Foreground" Value="{StaticResource BGWhiteBrush}"/>
    <Setter Property="BorderBrush" Value="{StaticResource FGBlackBrush}"/>
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type ButtonBase}">
                <Border x:Name="border" 
                            Background="{TemplateBinding Background}" 
                            BorderThickness="{TemplateBinding BorderThickness}" 
                            BorderBrush="{TemplateBinding BorderBrush}" 
                            SnapsToDevicePixels="false"         
                            RenderTransformOrigin="0.5,0.5"
                            RenderOptions.BitmapScalingMode="HighQuality">
                        <ContentPresenter x:Name="contentPresenter" Focusable="False" 
                                          HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" 
                                          Margin="{TemplateBinding Padding}" RecognizesAccessKey="True" 
                                          SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" 
                                          VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
                    <Border.RenderTransform>
                        <ScaleTransform/>
                    </Border.RenderTransform>
                </Border>
                <ControlTemplate.Triggers>
                    <!-- Button Mouse Hover Animation -->
                    <EventTrigger RoutedEvent="MouseEnter">
                        <BeginStoryboard>
                            <Storyboard >
                                <ColorAnimation Storyboard.TargetName="border"
                                                Storyboard.TargetProperty="Background.Color"
                                                To="{StaticResource Button.Hover.BGGray}"
                                                Duration="0:0:0.3" />
                                <DoubleAnimation Storyboard.TargetName="border"
                                                 Storyboard.TargetProperty="(RenderTransform).(ScaleTransform.ScaleX)"
                                                 To="1.05"
                                                 Duration="0:0:0.3"/>
                                <DoubleAnimation Storyboard.TargetName="border"
                                                 Storyboard.TargetProperty="(RenderTransform).(ScaleTransform.ScaleY)"
                                                 To="1.05"
                                                 Duration="0:0:0.3"/>
                            </Storyboard>
                        </BeginStoryboard>
                    </EventTrigger>
                    <EventTrigger RoutedEvent="MouseLeave">
                        <BeginStoryboard>
                            <Storyboard>
                                <ColorAnimation To="{StaticResource ButtonGray}"
                                                        Duration="0:0:0.4"
                                                        Storyboard.TargetName="border"
                                                        Storyboard.TargetProperty="Background.Color"/>
                                <DoubleAnimation Storyboard.TargetName="border"
                                                 Storyboard.TargetProperty="(RenderTransform).(ScaleTransform.ScaleX)"
                                                 To="1"
                                                 Duration="0:0:0.3"/>
                                <DoubleAnimation Storyboard.TargetName="border"
                                                 Storyboard.TargetProperty="(RenderTransform).(ScaleTransform.ScaleY)"
                                                 To="1"
                                                 Duration="0:0:0.3"/>
                            </Storyboard>
                        </BeginStoryboard>
                    </EventTrigger>
                    
                    <!-- Button Mouse Click Animation -->
                    <EventTrigger RoutedEvent="PreviewMouseDown">
                        <BeginStoryboard>
                            <Storyboard>
                                <ColorAnimation Storyboard.TargetName="border"
                                                Storyboard.TargetProperty="Background.Color"
                                                To="{StaticResource Button.Click.Gray}"
                                                Duration="0:0:0.2"/>
                                
                                <DoubleAnimation Storyboard.TargetName="border"
                                                 Storyboard.TargetProperty="(RenderTransform).(ScaleTransform.ScaleX)"
                                                 To="1"
                                                 Duration="0:0:0.1"/>
                                <DoubleAnimation Storyboard.TargetName="border"
                                                 Storyboard.TargetProperty="(RenderTransform).(ScaleTransform.ScaleY)"
                                                 To="1"
                                                 Duration="0:0:0.1"/>

                            </Storyboard>
                        </BeginStoryboard>
                    </EventTrigger>
                    <EventTrigger RoutedEvent="PreviewMouseUp">
                        <BeginStoryboard>
                            <Storyboard>
                                <ColorAnimation Storyboard.TargetName="border"
                                                Storyboard.TargetProperty="Background.Color"
                                                To="{StaticResource Button.Hover.BGGray}"
                                                Duration="0:0:0.1"/>
                                <DoubleAnimation Storyboard.TargetName="border"
                                                 Storyboard.TargetProperty="(RenderTransform).(ScaleTransform.ScaleX)"
                                                 Duration="0:0:0.1"
                                                 To="1.05"/>
                                <DoubleAnimation Storyboard.TargetName="border"
                                                 Storyboard.TargetProperty="(RenderTransform).(ScaleTransform.ScaleY)"
                                                 Duration="0:0:0.1"
                                                 To="1.05"/>
                            </Storyboard>
                        </BeginStoryboard>
                    </EventTrigger>
                    
                    <!-- Not Enabled -->
                    <Trigger Property="IsEnabled" Value="false">
                        <Trigger.EnterActions>
                            <BeginStoryboard>
                                <Storyboard>
                                    <ColorAnimation Storyboard.TargetName="border"
                                                    Storyboard.TargetProperty="Background.Color"
                                                    Duration="0:0:0.3"
                                                    To="{StaticResource Button.Disabled.Background}"/>
                                </Storyboard>
                            </BeginStoryboard>
                        </Trigger.EnterActions>
                        <Trigger.ExitActions>
                            <BeginStoryboard>
                                <Storyboard>
                                    <ColorAnimation Storyboard.TargetName="border"
                                                    Storyboard.TargetProperty="Background.Color"
                                                    Duration="0:0:0.3"
                                                    To="{StaticResource SmallButtonGray}"/>
                      
                                </Storyboard>
                            </BeginStoryboard>
                        </Trigger.ExitActions>
                    </Trigger>
                    
                    <!-- Enabled -->
                    <!-- Fade back into normal colors -->

                </ControlTemplate.Triggers>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>
liquidair
  • 177
  • 2
  • 11
  • Please avoid putting tags in your question titles as per the [tagging guidelines](https://stackoverflow.com/help/tagging) (see the last section headed "Should I use tags in titles?"). – ProgrammingLlama Jun 19 '21 at 16:17
  • Have you turned off the "native" triggers from the default template? Show completely the entire button template you are using. – EldHasp Jun 19 '21 at 16:36
  • @EldHasp: Good Question. I'm not sure but I updated the question with the entire button template as requested. – liquidair Jun 19 '21 at 16:49
  • In order to understand the problem, I need the entire template with all the resources to which it refers. So that I can copy it and check it on my computer. Let's say you animate the «Background.Color» property. But this property can only be animated if Background is set to an unfrozen SolidColorBrush instance. Or you set the animation `To` the Button.Disabled.Background resource, but it must be an instance of Media.Color. Many other errors are also possible, which would be difficult to detect without debugging. – EldHasp Jun 19 '21 at 17:23
  • @EldHasp: I updated again to show everything. WPF is seriously frustrating. How can I use the debugger to see what is going on so I don't have to bother you, haha? – liquidair Jun 19 '21 at 21:32
  • I don’t know how to debug animation in XAML step by step (similar to Sharp code). In fact, I just check the individual parts of code first, and then put everything together. Sometimes a part doesn't work. In other cases, the connection of correctly working parts can result in a non-working whole. – EldHasp Jun 19 '21 at 22:05
  • You are using the `ButtonGray`, `BGWhiteBrush`, `FGBlackBrush` resources. There are no such resources. Is this a mistake or did you miss them when you uploaded the code? – EldHasp Jun 19 '21 at 22:19

1 Answers1

1

For debugging purposes, I modified the template to see the property values of the brush instance in the Backgroud property you are trying to animate.

                <ControlTemplate TargetType="{x:Type ButtonBase}">
                    <Border x:Name="border" 
                            Background="{TemplateBinding Background}" 
                            BorderThickness="{TemplateBinding BorderThickness}" 
                            BorderBrush="{TemplateBinding BorderBrush}" 
                            SnapsToDevicePixels="false"         
                            RenderTransformOrigin="0.5,0.5"
                            RenderOptions.BitmapScalingMode="HighQuality">
                        <StackPanel>
                            <TextBlock Text="{Binding Background.Color, ElementName=border}"/>
                            <TextBlock Text="{Binding Background.IsFrozen, ElementName=border}"/>
                        </StackPanel>
                        <!--<ContentPresenter x:Name="contentPresenter" Focusable="False" 
                                          HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" 
                                          Margin="{TemplateBinding Padding}" RecognizesAccessKey="True" 
                                          SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" 
                                          VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>-->
                    </Border>
                    <ControlTemplate.Triggers>

Information was displayed that the SolidColorBrush instance (SmallButtonGrayBrush resource) was frozen. But frozen instances are immutable.

This happens because SmallButtonGrayBrush and SmallButtonGray resources are retrieved using StaticResource. If you receive resources using DynamicResource, then the instances are not frozen and the animation works.

    <!-- Gray Button Styles -->
    <Color x:Key="SmallButtonGray">#BB222222</Color>
    <SolidColorBrush x:Key="SmallButtonGrayBrush" Color="{DynamicResource SmallButtonGray}"/>
    <!-- Page Buttons-->
    <Style x:Key="GrayButtonStyle" TargetType="Button" BasedOn="{StaticResource SmallButtonStyle}">
        <Setter Property="Background" Value="{DynamicResource SmallButtonGrayBrush}"/>
EldHasp
  • 6,079
  • 2
  • 9
  • 24
  • Yup, that worked! Thank you! I swear there's all these little things in WPF that make it so maddeningly difficult. Like in that button template for the `MouseEnter` code, to animate the Background you use `Background.Color` but then `(RenderTransform).(ScaleTransform.ScaleX)` to scale and then if I want to do the text color (foreground) it's not `Foreground.Color` like it is to set the color, for some reason it is `(TextElement.Foreground).(SolidColorBrush.Color)`!! – liquidair Jun 19 '21 at 23:44
  • This is not a problem with WPF itself. Such rules for specifying the path in the PropertyPath class. And WPF just uses this class for its own purposes. Ditto for XPath in Binding. This is also not invented for WPF, but WPF uses it with all its inherent features. – EldHasp Jun 19 '21 at 23:51
  • The Freezable class derives from DependecyObject. But DependecyObject don't allow you to work with yourself from different threads. This is often very important to improve productivity. For this, Freezable was developed. As long as it is not frozen it is similar to DO and can be mutable (including using bindings). But if it needs to be passed to another thread, then it is frozen first. Then it can be used as a regular CLR type. But in some cases, the incompletely clear conditions under which WPF (the XAML designer) thinks the instance should be frozen lead to problems. – EldHasp Jun 19 '21 at 23:57