Animation Handoff Bug and Workaround for current WPF/E CTP

My good friend Hans Hugli, who doesn't have a blog but sits down the hall from me, hit the following issue with the current CTP of WPF/E and wanted to get it out to the world, so I'm posting it on his behalf. You can see what he's talking about in action (source also) here

 

***************************************

 

I set out to create a slideshow of Flickr images with transitions using WPFe, and I quickly ran into a problem which prevented me from being able do animation beyond simple animation (an element moving from point A to point B and back again or repeating it’s movement.) I needed move element through an arbitrary set of points, so that I could do several scales, translations, and rotations consecutively.

 

The behavior that I was seeing was that the first Storyboard animation would happen after calling Begin(), but all other Storyboard Begin() calls would be ignored. Of course, my first thought was that I was doing something wrong, so I scoured the code. I thought that perhaps the Timeline associated with the Storyboard needed to be reset. But apparently the Begin() method resets the TimeLine. I played around with the multiple ways that you can define values in the DoubleAnimation class: By, From, To. From and To are explicit position values and By is a relative movement from the target elements starting property value. But neither solved the issue.

 

I then stumbled across a solution a few hours later. By stopping the Storyboard immediately before calling the Begin() method of the next Storyboard the animations successfully and smoothly transitioned from the first animation to the next.

 

It turns out that it was a WPFe bug. The post animation target element property values for the Storyboard were not being handed off to the next Storyboard being called. This bug was fixed shortly after the December CTP, and it should make it into the next CTP.

 

So let’s say you want to move a rectangle from point A to point B and then to point C and then back to A again. I would do it so:

 

Environment.js:

    function root_Loaded(s, a) {

        StartNextAnimation(1);  

    }

    function anim_completed(s,a) {

        switch(s.Name){

            case "sb1":

                setTimeout("StartNextAnimation(2)", 2000); // two second pause between animations.

                break;

            case "sb2":

                setTimeout("StartNextAnimation(3)", 2000);

                break;

            case "sb3":

                setTimeout("StartNextAnimation(1)", 2000);

                break;

        }

    }

    function StartNextAnimation(s) {

        switch(s){

            case 1:

                sb3.Stop(); // previous Storyboard animation must be stopped immediately prior to next Storyboard animation's Begin() method for smooth positioning behavior.

                sb1.Begin();

            break;

            case 2:

                sb1.Stop();

                sb2.Begin();

            break;

            case 3:

                sb2.Stop();

                sb3.Begin();

            break;

        }

    }

 

Multpointanimation.xml:

<Canvas … >

       <TriggerActionCollection>

          <BeginStoryboard>

            <Storyboard x:Name="sb1" BeginTime="1" Completed="javascript:anim_completed">

              <DoubleAnimation Storyboard.TargetName="Rectangle1" Storyboard.TargetProperty="(Canvas.Left)" From="0" To="400" Duration="0:0:1"/>

            </Storyboard>

          </BeginStoryboard>

          <BeginStoryboard>

            <Storyboard x:Name="sb2" BeginTime="1" Completed="javascript:anim_completed">

              <DoubleAnimation Storyboard.TargetName="Rectangle1" Storyboard.TargetProperty="(Canvas.Left)" From="400" To="200" Duration="0:0:1"/>

            </Storyboard>

          </BeginStoryboard>

          <BeginStoryboard>

            <Storyboard x:Name="sb3" BeginTime="1" Completed="javascript:anim_completed">

              <DoubleAnimation Storyboard.TargetName="Rectangle1" Storyboard.TargetProperty="(Canvas.Left)" From="200" To="0" Duration="0:0:1"/>

            </Storyboard>

          </BeginStoryboard>

        </TriggerActionCollection>

 

    <Rectangle Width="40" Height="30" Fill="Red" x:Name="Rectangle1"/>

 

</Canvas>