Search code examples
c#animationuwprendertransform

UWP: How to animate a RenderTranform and keep it usable after that?


I have a very simple project where I need to animate the RenderTransform of an element, and then further manipulate such transform.

Please find as a reference an MVCE here: https://github.com/cghersi/UWPExamples/tree/master/RenderTransformAnimation.

The scenario is the following: there is a ScrollViewer m_scrollView, with a Canvas content m_zoomView. For the sake of the example we also have a CompositeTransform m_zoomViewTransform = m_zoomView.RenderTransform.

I use the following method to manipulate the RenderTransform, either with or without an animation:

private void SetEffectiveOffsetOfScrollView(Point newOffset, bool isAnimated)
{
    if (isAnimated)
    {
        TimeSpan dur = TimeSpan.FromSeconds(0.2);
        Storyboard sb = new Storyboard { Duration = dur };
        DoubleAnimation animationX = new DoubleAnimation
        {
            To = newOffset.X,
            Duration = dur,
            AutoReverse = false
        };
        DoubleAnimation animationY = new DoubleAnimation
        {
            To = newOffset.Y,
            Duration = dur,
            AutoReverse = false
        };
        sb.Children.Add(animationX);
        sb.Children.Add(animationY);
        Storyboard.SetTarget(animationX, m_zoomViewTransform);
        Storyboard.SetTarget(animationY, m_zoomViewTransform);
        Storyboard.SetTargetProperty(animationX, "CompositeTransform.TranslateX");
        Storyboard.SetTargetProperty(animationY, "CompositeTransform.TranslateY");

        sb.Begin();
        sb.Completed += (sender, o) =>
        {
            m_zoomViewTransform.TranslateX = newOffset.X;
            m_zoomViewTransform.TranslateY = newOffset.Y;
        };
    }
    else
    {
        m_zoomViewTransform.TranslateX = newOffset.X;
        m_zoomViewTransform.TranslateY = newOffset.Y;
    }
}

Now, if I use SetEffectiveOffsetOfScrollView() with isAnimated = true, I am not able to change the RenderTransform anymore, or at least I don't see any update to the UI anymore.

In the MVCE I added a button that invokes the SetEffectiveOffsetOfScrollView() method with isAnimated = true, and I added a Manipulation event to pan the m_zoomView Canvas: as soon as I click on the button, I am no more able to pan the Canvas.

How can I animate the transformation, still being able to see the updates to the UI after this action, using SetEffectiveOffsetOfScrollView() with animate=false?


Solution

  • This is due to dependency property value precedence, as described here: https://learn.microsoft.com/en-us/windows/uwp/xaml-platform/dependency-properties-overview#dependency-property-value-precedence

    In this repro, the Storyboard is still active, due to the default FillBehavior=HoldEnd on the DoubleAnimations. Since those animations are still alive, the animated value gets used, even as new local values get set on the isAnimated=false case.

    The easy fix is to call sb.Stop() in the Storyboard's Completed handler, after you've set the new local values to hold. This will stop the animations, removing the Animated values they are holding, and allow the Local values to be used.