Question

How can I remove a storyboard in XAML (i.e. RemoveStoryboard action in a DataTrigger) but keep the value that was animated. Similar to the Animatable.BeginAnimation:

If the animation's BeginTime is null, any current animations will be removed and the current value of the property will be held.

Was it helpful?

Solution

RemoveStoryboard's primary use is to remove the animated values and set them back to their un-animated state. In most cases you can just switch the call to PauseStoryboard or StopStoryboard instead, depending on the specific case. The only exception is when you need to free up resources held by the storyboard or use it for another purpose.

If you really want to remove the storyboard and keep the property values, you must set the animated values directly on the properties. This can be done by setting each value to the animated value, something like this:

void CopyAnimatedValuesToLocalValues(DependencyObject obj)
{
  // Recurse down tree
  for(int i=0; i<VisualTreeHelper.GetChildrenCount(obj); i++)
    CopyAnimatedValuesToLocalValues(VisualTreeHelper.GetChild(obj, i));

  var enumerator = obj.GetLocalValueEnumerator();
  while(enumerator.MoveNext())
  {
    var prop = enumerator.Current.Property;
    var value = enumerator.Current.Value as Freezable;

    // Recurse into eg. brushes that may be set by storyboard, as long as they aren't frozen
    if(value!=null && !value.IsFrozen)
      CopyAnimatedValuesToLocalValues(value);

    // *** This is the key bit of code ***
    if(DependencyPropertyHelper.GetValueSource(obj, prop).IsAnimated)
      obj.SetValue(prop, obj.GetValue(prop));

  }
}

Call this right before you remove the storyboard to copy the animated values.

Edit A comment was made that this code may be unnecessary because calling BeginAnimation with BeginTime=null achieves a similar effect.

While it is true that BeginAnimation with BeginTime=null makes it looks as if the values were copied to local, a later call to RemoveStoryboard will cause the values to revert. This is because BeginAnimation with BeginTime=null causes the prior animation to hold its values pending the start of the new animation, but does nothing to affect the local values.

The code above actually overwrites local values, so all animations can be removed and the objects will still have their new values. So if you really want to call RemoveStoryboard and still keep your values, you will need the code I wrote above or something like it.

OTHER TIPS

I had a similar problem when using AnimationTimeline. The easiest solution turned out to be capturing the Completed event in code behind and prior to calling BeginAnimation with the null parameter to remove the animation, get the property's current value and use that to set it.

This gets the last animated value, and sets it.

void OnCompleted( object sender, EventArgs args )
{
    // Required to copy latest animated value to local value.
    o.SomeValue = o.SomeValue;
    o.BeginAnimation( SomeClass.SomeValueProperty, null );
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top