Question

I have asked a question here but then I realized my problem was not the code but the style I am using for a button. Since the problem is completely different than the one initially asked, I thought it would be more beneficial for other users if I just asked the "right" question again. Here I go:

I am using the template below in my button. When I set button.IsEnabled=false it works ok but if I set button.IsEnabled=true it doesn't get enabled. Can you please pinpoint what I am doing wrong? Thanks

<Style x:Key="BlackButtonStyle" TargetType="{x:Type Button}">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type Button}">
                <ControlTemplate.Resources>
                    <Storyboard x:Key="MouseOverActivating">
                        <ColorAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="rectangle" Storyboard.TargetProperty="(Shape.Fill).(GradientBrush.GradientStops)[1].(GradientStop.Color)">
                            <SplineColorKeyFrame KeyTime="00:00:00" Value="#FF2F2F2F"/>
                            <SplineColorKeyFrame KeyTime="00:00:00.1270000" Value="#FF2391FF"/>
                        </ColorAnimationUsingKeyFrames>
                    </Storyboard>
                    <Storyboard x:Key="MouseOverDeactivating">
                        <ColorAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetProperty="(Shape.Fill).(GradientBrush.GradientStops)[1].(GradientStop.Color)" Storyboard.TargetName="rectangle">
                            <SplineColorKeyFrame KeyTime="00:00:00" Value="#FF2391FF"/>
                            <SplineColorKeyFrame KeyTime="00:00:00.2200000" Value="#FF2F2F2F"/>

                        </ColorAnimationUsingKeyFrames>
                    </Storyboard>
                    <Storyboard x:Key="PressActivating">
                        <ColorAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="rectangle" Storyboard.TargetProperty="(Shape.Fill).(GradientBrush.GradientStops)[1].(GradientStop.Color)">
                            <SplineColorKeyFrame KeyTime="00:00:00" Value="#FF2391FF"/>
                            <SplineColorKeyFrame KeyTime="00:00:00.1370000" Value="#FF48D6FF"/>
                        </ColorAnimationUsingKeyFrames>
                    </Storyboard>
                    <Storyboard x:Key="PressedDeactivating" FillBehavior="Stop" >
                        <ColorAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetProperty="(Shape.Fill).(GradientBrush.GradientStops)[1].(GradientStop.Color)" Storyboard.TargetName="rectangle">
                            <SplineColorKeyFrame KeyTime="00:00:00" Value="#FF48D6FF"/>
                            <SplineColorKeyFrame KeyTime="00:00:00.2370000" Value="#FF2391FF"/>
                        </ColorAnimationUsingKeyFrames>
                    </Storyboard>
                    <Storyboard x:Key="DisableActivating">
                        <ColorAnimationUsingKeyFrames BeginTime="00:00:00" Duration="00:00:00.0010000" Storyboard.TargetName="rectangle" Storyboard.TargetProperty="(Shape.Fill).(GradientBrush.GradientStops)[1].(GradientStop.Color)">
                            <SplineColorKeyFrame KeyTime="00:00:00" Value="#FFA7A7A7"/>
                        </ColorAnimationUsingKeyFrames>
                    </Storyboard>
                </ControlTemplate.Resources>
                <Grid>
                    <Rectangle Stroke="Transparent" RadiusX="5" RadiusY="5" x:Name="rectangle">
                        <Rectangle.Fill>
                            <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
                                <GradientStop Color="#FF000000" Offset="0"/>
                                <GradientStop Color="#FF2F2F2F" Offset="1"/>
                            </LinearGradientBrush>
                        </Rectangle.Fill>
                    </Rectangle>
                    <ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" RecognizesAccessKey="True" OpacityMask="{x:Null}"/>
                    <Rectangle Stroke="Transparent" RadiusX="5" RadiusY="5" x:Name="WhiteGlow">
                        <Rectangle.Fill>
                            <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
                                <GradientStop Color="#5BFFFFFF" Offset="0"/>
                                <GradientStop Color="#00FFFFFF" Offset="0.5"/>
                            </LinearGradientBrush>
                        </Rectangle.Fill>
                    </Rectangle>
                </Grid>
                <ControlTemplate.Triggers>
                    <Trigger Property="IsCancel" Value="False"/>
                    <EventTrigger RoutedEvent="FrameworkElement.Loaded"/>
                    <Trigger Property="IsFocused" Value="True">
                        <Trigger.ExitActions>
                            <BeginStoryboard Storyboard="{StaticResource MouseOverActivating}" x:Name="MouseOverActivating_BeginStoryboard2"/>
                        </Trigger.ExitActions>
                        <Trigger.EnterActions>
                            <BeginStoryboard Storyboard="{StaticResource MouseOverActivating}" x:Name="MouseOverActivating_BeginStoryboard1"/>
                        </Trigger.EnterActions>
                    </Trigger>
                    <Trigger Property="IsDefaulted" Value="True"/>
                    <Trigger Property="IsMouseOver" Value="True">
                        <Trigger.ExitActions>
                            <BeginStoryboard Storyboard="{StaticResource MouseOverDeactivating}" x:Name="MouseOverDeactivating_BeginStoryboard"/>
                        </Trigger.ExitActions>
                        <Trigger.EnterActions>
                            <BeginStoryboard Storyboard="{StaticResource MouseOverActivating}" x:Name="MouseOverActivating_BeginStoryboard"/>
                        </Trigger.EnterActions>
                    </Trigger>
                    <Trigger Property="IsPressed" Value="True">
                        <Trigger.EnterActions>
                            <BeginStoryboard x:Name="PressActivating_BeginStoryboard" Storyboard="{StaticResource PressActivating}"/>
                        </Trigger.EnterActions>
                        <Trigger.ExitActions>
                            <BeginStoryboard x:Name="PressedDeactivating_BeginStoryboard" Storyboard="{StaticResource PressedDeactivating}"/>
                        </Trigger.ExitActions>
                    </Trigger>
                    <Trigger Property="IsEnabled" Value="False">
                        <Trigger.EnterActions>
                            <BeginStoryboard Storyboard="{StaticResource DisableActivating}" x:Name="DisableActivating_BeginStoryboard"/>
                        </Trigger.EnterActions>
                    </Trigger>
                </ControlTemplate.Triggers>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>
Was it helpful?

Solution

I suspect that the behaviour you are seeing is a result of the animation you use when IsEnabled becomes false. DependencyProperties actually have a precedence associated to them, in which animations are high up on the list; that hierarchy, from MSDN, is:

  • Property system coercion
  • Active animations, or animations with a Hold behavior.
  • Local value
  • TemplatedParent template properties
  • Implicit style
  • Style triggers
  • Template triggers
  • Style setters
  • Default (theme) style
  • Inherited from parent
  • Default value from dependency property metadata

By default, animations have a FillBehavior of HoldEnd, which means that they stay at the value that the animation ended at. When IsEnabled becomes True through your binding, that update occurs at the 'Local value' level in the precedence, and since the DisableActivating storyboard is holding the appearance at a higher level of precedence ('animations with a Hold behavior'), you never see the button change once it has changed the first time.

There are three solutions to this:

  1. Update your animation to have a FillBehavior of Stop, which means that the animation will not assert the 'IsEnabled=False' visual once the animation stops. You'll need a standard, non-animated trigger with the same state as the end of the animation to continue to assert this once the animation completes, as otherwise you'll just see it revert back to the state it was when the animation began. Since it's just a trigger set, when the local value is updated you'll see it return to the original value like you expect. Also, since the animation is of higher precedence, you can set the style and start the animation simultaneously and only "see" the affects of the style once the animation completes (so your fade will work as expected).
  2. Instead of changing the FillBehavior, you can create a new trigger that applies when IsEnabled is True that animates (perhaps instantaneously) back to the original state. This can also be done by applying an animation in the trigger's ExitAction. Since it's also an animation but was applied later, it will override the HoldEnd state of the other animation. In some sense, this is easier that option 1, but it can become a hassle to maintain a forward and reverse animation, especially if you don't need the reverse animation for a specific visual effect; you may want to keep it to fade in and out the disabled state, however.
  3. Add an ExitAction to your IsEnabled trigger to stop the storyboard, preventing the animation from continuing to assert the value it had at the end of the animation so that the local value style can be applied. This option has the benefits of not having to repeat the style (as in #1) while also not having to reverse the animation (as in #2).

Of the three solutions, the last one is probably the cleanest architecturally (unless you have a specific reason, like needing to fade both in and out, to prefer one of the other options - or a combination of the options above).

OTHER TIPS

The easiest option is to add an animation the the IsEnabled trigger ExitAction that will revert the animation in the EnterAction

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top