When I set a property in code after it has been animated it doesn’t work. What is going on?

WPF’s animation system is used for changing properties such as width, position or color of an element over time. When animations finish they can either return the property being animated to its original value, or “hold” their final value. This behavior is controlled by the FillBehavior property of the animation. After animating properties where the animation holds the final value developers can run into problems when they try to change those very same properties via code. The changes seem to have no effect!


This strange behavior is a side-effect of the WPF dependency property system.  The dependency property system is a special property system built by the WPF team. It allows element properties to be inherited from their container element, animated, data bound and set via regular .NET code. One side-effect of this kind of property system is that a single property on an element could get its value from a number of potential sources.  In the regular .NET property system property changes are fairly transparent, with more recent changes over-writing previous ones. The more declarative nature of WPF makes this temporal-based property system less useful. Instead the value an element property has is based on a precedence system which is detailed here. Animations have a higher precedence than everything else, so changes made by them always over-ride other possible property values. While this seems sensible and in most cases gives us the outcome we would expect it can be a little confusing for animations that hold their value after they complete. When the animation ends it holds the property at a certain value, and because animations takes precedence over other ways properties can be set it prevents the property being changed through normal .NET code.

It is possible to change this using a little code to remove the animation when it completes, however a better approach is to not use the HoldEnd fill behavior if you need to subsequently change a value in code. Instead you should use the Stop FillBehavior and “fake” the HoldEnd by handling the completed event for the animation and setting the property value to the value you wish to finish at.

The following code shows a text block that has its FontSize animated to 35 pixels when it loads.

<TextBlock Grid.Row="0" Text="WPF is great" FontFamily="Lucida Sans" Foreground="#feca00" HorizontalAlignment="Center" Name="windowHeading">
  <TextBlock.Triggers>
    <EventTrigger RoutedEvent="FrameworkElement.Loaded">
      <EventTrigger.Actions>
        <BeginStoryboard Name="windowHeadingStoryboard">
          <Storyboard>
          <DoubleAnimation To="35" Storyboard.TargetName="windowHeading"
         Storyboard.TargetProperty="FontSize" Duration="0:0:5"
         Completed="FinishedAnimation" FillBehavior="Stop" />

          </Storyboard>
        </BeginStoryboard>
      </EventTrigger.Actions>
    </EventTrigger>
  </TextBlock.Triggers>
</TextBlock>

The very simple C# code to simulate the HoldEnd behavior is shown here in the Completed event handler for the animation.

void FinishedAnimation(object sender, EventArgs e)
{
    windowHeading.FontSize = 35;
}

The obvious drawback with this approach is the fairly tight coupling between the markup and application code that is required for it to work.