Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Animation width to actual width in WPF?

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}"
like image 517
Tower Avatar asked Jun 12 '12 10:06

Tower


2 Answers

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}}"/>
like image 55
Andy Avatar answered Sep 28 '22 10:09

Andy


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.

like image 26
H.B. Avatar answered Sep 28 '22 11:09

H.B.