Themelis
Themelis

Reputation: 4255

Why I can't use DoubleAnimationUsingPath to animate the TranslateForm.X propertry?

I created a custom Panel in wpf where the children need to be animated after the rendering. The animation I want for each child is to translate (move) using a bezier curve.

I followed Microsoft's example and created the AddAnimation method where I am adding the Path where I want every item of the panel to be animated.

Unfortunatelly I get the bellow exception:

Cannot animate the 'X' property on a 'System.Windows.Media.TranslateTransform' using a 'System.Windows.Media.Animation.DoubleAnimationUsingPath'

But how am I supposed to animate the X property without a DoubleAnimationUsingPath?

Here is that method:

private void AddAnimation(FrameworkElement child, Point childLocation)
        {
            var middlePoint = GetMiddlePoint();
            var finalPoint = GetFinalPoint();

            var animatedTranslateTransform = new TranslateTransform();
            child.RenderTransform = animatedTranslateTransform;
            child.RenderTransformOrigin = new Point(0.5, 0.5);

            // Create the animation path.
            PathGeometry animationPath = new PathGeometry();
            PathFigure pFigure = new PathFigure();
            pFigure.StartPoint = childLocation;
            PolyBezierSegment pBezierSegment = new PolyBezierSegment();
            pBezierSegment.Points.Add(middlePoint);
            pBezierSegment.Points.Add(finalPoint);
            pFigure.Segments.Add(pBezierSegment);
            animationPath.Figures.Add(pFigure);

            // Freeze the PathGeometry for performance benefits.
            animationPath.Freeze();

            // Create a DoubleAnimationUsingPath to move the
            // rectangle horizontally along the path by animating 
            // its TranslateTransform.
            DoubleAnimationUsingPath translateXAnimation =
                new DoubleAnimationUsingPath();
            translateXAnimation.PathGeometry = animationPath;
            translateXAnimation.Duration = TimeSpan.FromSeconds(5);

            // Set the Source property to X. This makes
            // the animation generate horizontal offset values from
            // the path information. 
            translateXAnimation.Source = PathAnimationSource.X;

            // Set the animation to target the X property
            Storyboard.SetTarget(translateXAnimation, child);
            Storyboard.SetTargetProperty(translateXAnimation,
                new PropertyPath("(FrameworkElement.RenderTransform).(TranslateTransform.X)"));

            // Create a DoubleAnimationUsingPath to move the
            // rectangle vertically along the path by animating 
            // its TranslateTransform.
            DoubleAnimationUsingPath translateYAnimation =
                new DoubleAnimationUsingPath();
            translateYAnimation.PathGeometry = animationPath;
            translateYAnimation.Duration = TimeSpan.FromSeconds(5);

            // Set the Source property to Y. This makes
            // the animation generate vertical offset values from
            // the path information. 
            translateYAnimation.Source = PathAnimationSource.Y;

            // Set the animation to target the Y property
            // of the TranslateTransform named "AnimatedTranslateTransform".
            Storyboard.SetTarget(translateYAnimation, child);
            Storyboard.SetTargetProperty(translateYAnimation,
                new PropertyPath("(FrameworkElement.RenderTransform).(TranslateTransform.Y)"));

            // Create a Storyboard to contain and apply the animations.
            Storyboard pathAnimationStoryboard = new Storyboard();
            pathAnimationStoryboard.RepeatBehavior = RepeatBehavior.Forever;
            pathAnimationStoryboard.Children.Add(translateXAnimation);
            pathAnimationStoryboard.Children.Add(translateYAnimation);

            // Start the animations when the rectangle is loaded.
            child.Loaded += (sender, args) =>
            {
                Debug.WriteLine($"Child {child} loaded.");
                pathAnimationStoryboard.Begin(child);
            };
        }

Upvotes: 0

Views: 472

Answers (1)

Clemens
Clemens

Reputation: 128147

Take a look at the InnerException property of the AnimationException.

The cause of the exception is that two points are not enough for a PolyBezierSegment, because it needs two control points and an end point, hence three points per curve.

Use a PolyQuadraticBezierSegment instead:

var pBezierSegment = new PolyQuadraticBezierSegment();
pBezierSegment.Points.Add(middlePoint);
pBezierSegment.Points.Add(finalPoint);

Or just a QuadraticBezierSegment:

var bezierSegment = new QuadraticBezierSegment();
bezierSegment.Point1 = middlePoint;
bezierSegment.Point2 = finalPoint;

The code seems also far too complicated for what it is supposed to do. You may replace the Storyboard with two DoubleAnimationUsingPath children with a single MatrixAnimationUsingPath that animates the Matrix property of a MatrixTransform:

var animation = new MatrixAnimationUsingPath
{
    PathGeometry = animationPath,
    Duration = TimeSpan.FromSeconds(5)
};

var transform = new MatrixTransform();

child.RenderTransform = transform;
child.Loaded += (s, e) =>
    transform.BeginAnimation(MatrixTransform.MatrixProperty, animation);

Upvotes: 2

Related Questions