How can I animate the width of an element from 0 to its actual width in WPF?
I tried this:
<ControlTemplate.Triggers>
<EventTrigger RoutedEvent="Loaded">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Duration="0:0:0.3" To="{Binding ElementName=MyElement, Path=ActualWidth}" From="0" Storyboard.TargetProperty="Width" Storyboard.TargetName="MyElement" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</ControlTemplate.Triggers>
If I change the binding to a hard coded value, like 100
, then the width is properly animated, except that I want to bind to the actual width of the element.
If it matters, MyElement
is a border, and I'm animating a tab item.
For the record, this does not work either:
To="{Binding RelativeSource={RelativeSource AncestorType={x:Type Border}}, Path=ActualWidth}"
I'm sure this is wrong for SO many reasons and please feel free to tell me how many WPF laws I've violated but.. I solved the same problem by creating my own BindableDoubleAnimation.
public class BindableDoubleAnimation : DoubleAnimationBase
{
DoubleAnimation internalAnimation;
public DoubleAnimation InternalAnimation { get { return internalAnimation; } }
public double To
{
get { return (double)GetValue(ToProperty); }
set { SetValue(ToProperty, value); }
}
/// <summary>
/// Dependency backing property for the <see cref="To"/> property.
/// </summary>
public static readonly DependencyProperty ToProperty =
DependencyProperty.Register("To", typeof(double), typeof(BindableDoubleAnimation), new UIPropertyMetadata(0d, new PropertyChangedCallback((s, e) =>
{
BindableDoubleAnimation sender = (BindableDoubleAnimation)s;
sender.internalAnimation.To = (double)e.NewValue;
})));
public double From
{
get { return (double)GetValue(FromProperty); }
set { SetValue(FromProperty, value); }
}
/// <summary>
/// Dependency backing property for the <see cref="From"/> property.
/// </summary>
public static readonly DependencyProperty FromProperty =
DependencyProperty.Register("From", typeof(double), typeof(BindableDoubleAnimation), new UIPropertyMetadata(0d, new PropertyChangedCallback((s, e) =>
{
BindableDoubleAnimation sender = (BindableDoubleAnimation)s;
sender.internalAnimation.From = (double)e.NewValue;
})));
public BindableDoubleAnimation()
{
internalAnimation = new DoubleAnimation();
}
protected override double GetCurrentValueCore(double defaultOriginValue, double defaultDestinationValue, AnimationClock animationClock)
{
return internalAnimation.GetCurrentValue(defaultOriginValue, defaultDestinationValue, animationClock);
}
protected override Freezable CreateInstanceCore()
{
return internalAnimation.Clone();;
}
}
I'm now free to use bindings for the To From properties.
<local:BindableDoubleAnimation Storyboard.TargetProperty="Width" From="0" To="{Binding ActualWidth, RelativeSource={RelativeSource AncestorType=Window}}"/>
That bindings cannot be used in animations is a common problem.
One workaround would be that you use a helper object which has SourceValue
& TargetValue
(which you can bind to whatever you want, including ActualWidth
) a CurrentValue
(which returns the animated value) and a Progress
which can be animated from 0.0 to 100.0. When the progress is changed you just need to calculate the CurrentValue
to which you then can bind the Width
of your object. It is not pretty but should work.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With