Silverlight 2 Samples: Animating Controls

UPDATE: Get the latest animating control code from Blacklight, our new CodePlex project!  

Whilst developing the MSCUI Patient Journey Demonstrator, we made use of animation in a number of places to show how the user interface is moving and adapting to the user tasks. Animation is a great tool for showing users how the application changes, allowing them to watch how the content changes size and position, without it ‘clunking’ into place and the user losing sight of everything.

I wanted to create an animation system that allowed us to consistently change a controls size and position, with animation, that was very easy and natural to work with in code. Something I wanted to prevent was the need to duplicate storyboard XAML for each and every control that we wanted to animate.

The approach I decided to take was to create a new class that extended Control- called AnimatedControl. The new class would ultimately add 2 public methods to Control:

· public void AnimateSize(double width, double height)

· public void AnimatePosition(double x, double y)

...as well as some additional properties that would allow me to change the duration of the animations.

AnimatedControl works by creating a Canvas in the AnimatedControl constructor which contains a Rectangle and 2 storyboards. The Canvas is created using a XAML string and XamlReader.Load(...). The XAML string used is shown below...

<!--

        Canvas containing rectangle for control animation

      -->

      <Canvas xmlns="https://schemas.microsoft.com/client/2007"

                xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml">

        <Canvas.Resources>

         

          <!-- Size animation storyboard -->

          <Storyboard x:Key="sizeStoryboard" BeginTime="00:00:00">

            <!-- Animates the rectangles width -->

            <DoubleAnimationUsingKeyFrames

              Storyboard.TargetName="animatedElement"

              Storyboard.TargetProperty="(FrameworkElement.Width)">

              <SplineDoubleKeyFrame Value="0"

              KeyTime="00:00:0.5"

                                    KeySpline="0.528,0,0.142,0.847" />

           

            </DoubleAnimationUsingKeyFrames>

           

            <!-- Animates the rectangles height -->

            <DoubleAnimationUsingKeyFrames

              Storyboard.TargetName="animatedElement"

              Storyboard.TargetProperty="(FrameworkElement.Height)">

              <SplineDoubleKeyFrame Value="0"

                          KeyTime="00:00:0.5"

                                    KeySpline="0.528,0,0.142,0.847" />

            </DoubleAnimationUsingKeyFrames>

          </Storyboard>

         

          <!-- Position animation storyboard -->

          <Storyboard x:Key="positionStoryboard" BeginTime="00:00:00">

            <!-- Animates the rectangles X position in a Canvas -->

            <DoubleAnimationUsingKeyFrames

              Storyboard.TargetName="animatedElement"

            Storyboard.TargetProperty="(Canvas.Left)">

              <SplineDoubleKeyFrame Value="0"

                                    KeyTime="00:00:0.5"

                                    KeySpline="0.528,0,0.142,0.847" />

            </DoubleAnimationUsingKeyFrames>

            <!-- Animates the rectangles Y position in a Canvas -->

            <DoubleAnimationUsingKeyFrames

              Storyboard.TargetName="animatedElement"

              Storyboard.TargetProperty="(Canvas.Top)">

              <SplineDoubleKeyFrame Value="0"

                                    KeyTime="00:00:0.5"

                                    KeySpline="0.528,0,0.142,0.847" />

            </DoubleAnimationUsingKeyFrames>

          </Storyboard>

        </Canvas.Resources>

        <!-- Hidden rectangle -->

        <Rectangle x:Name="animatedElement"

                   Height="0" Width="0"

                   Canvas.Top="0" Canvas.Left="0" />

      </Canvas>

Once the Canvas has been created, the important elements are fished out and stored in private members. The code below shows creating the canvas from the XAML string below, storing a reference to the storyboards and finally hooking up their completed events...

// Create a canvas from the XAML

Canvas animatedElement = XamlReader.Load(animatedElementXaml) as Canvas;

// Pull out the rectangle into a memeber

this.animatedElement = animatedElement.Children[0] as Rectangle;

// Pull out the storyboards into a member and hook up completed events

this.sizeAnimation = animatedElement.Resources["sizeStoryboard"]

as Storyboard;

this.sizeAnimation.Completed += new EventHandler(sizeAnimation_Completed);

               

this.positionAnimation = animatedElement.Resources["positionStoryboard"]

                    as Storyboard;

this.positionAnimation.Completed += new EventHandler(positionAnimation_Completed);

When AnimateSize or AnimatePosition is called on the control, the corresponding storyboard is kicked off in the Canvas, changing the properties of the rectangle. At the same time, a DispatcherTimer is started that ticks ‘as quickly as possible’ to update the controls size and position to match that of the rectangles, as shown below...

void timer_Tick(object sender, EventArgs e)

{

// If the size is animating, update the controls size

if (this.sizeAnimating)

{

this.Width = this.animatedElement.Width;

this.Height = this.animatedElement.Height;

}

// If the position is animating, update the control position

if (this.positionAnimating)

{

Canvas.SetLeft(this, Canvas.GetLeft(this.animatedElement));

Canvas.SetTop(this, Canvas.GetTop(this.animatedElement));

}

}

Now with each tick of the timer, the controls size and position will animate. When the size and position animations have both completed, the timer will stop ticking.

There are some requirements and limitations with my AnimatedControl that I have listed here, but I have suggested some additions too...

· The Position animation only changes the Canvas.Left and Canvas.Top properties. So, if you control is not in a Canvas (or a panel that derives Canvas), it won’t move. AnimatedControl could indeed be extended however, to animate the controls render transform to move it if it is not in a Canvas...

· AnimatedControl currently extends Control, so if you wanted an AnimatedControl that extends ContentControl, or UserControl, you would need to copy the code over to a new class - updating then becomes a pain.

· AnimatedControl only supports one KeyFrame at the moment. Again, this could be updated by adding and removing key frames in the AnimateSize / AnimatePosition methods.

· You can of course extend AnimatedControl to animate more properties such as Opacity, ScaleTransform, Background color etc.

I have attached a project here that very simply, shows AnimatedControl working on some basic controls (just rectangles really!). You can also see the example project running here.

Please enjoy and feedback any additions, bugs etc.

Thanks,

Martin

AnimatedControlSample.zip

Comments

  • Anonymous
    June 23, 2008
    PingBack from http://wordnew.acne-reveiw.info/?p=1990

  • Anonymous
    June 23, 2008
    Martin Graysonのブログ に MSCUI でも使われている アニメーションするコントロールのサンプル が公開されています。 使い方としては、XAML内で以下のように宣言します。 &lt; Canvas

  • Anonymous
    June 24, 2008
    Trackback from SilverlightShow.net

  • Anonymous
    June 27, 2008
    Hello!I got the most reliable knowledge about Softimage 3D software is a part of Avid. The developers of this software claim mixed pipeline management leading to more efficiency, less time for development and uniquely suited for game development.Thank you!

  • Anonymous
    August 01, 2008
    A control that we used in a number of places in the MSCUI Patient Journey Demonstrator was the dragging,

  • Anonymous
    August 06, 2008
    I receive "Assembly 'AnimatedControlSample' was not found." when loading your demo. Is that cause I'm on Beta 2? How can I fix this? Thanks.

  • Anonymous
    August 06, 2008
    Hi duke63, I tried downloading the source I attached, unzipped, opened in VS2008 (with SL2 B2 tools installed), hit F5 and the project ran fine. Did you install the tools chainer from http://www.microsoft.com/downloads/details.aspx?FamilyId=50A9EC01-267B-4521-B7D7-C0DBA8866434&displaylang=en ? This install includes all that you need - the developer runtime, the SDK, the VS templates and patches etc. Let me know if you still have any issues. Martin

  • Anonymous
    August 09, 2008
    While clever, I'm not sure why you found it necessary to create, essentially, TWO animations, where it seems that one -- animating the control directly with a Storyboard -- would do. I'd be interested in knowing your reasoning for animating the control in this way: mirroring of the Storyboard animation of the internal rectangle using a timer. Just seems unnecessary. But maybe I'm missing your intent. What problem were you trying to solve? And could this be achieved with a much lighter weight approach? Say, using a couple of extension methods? Best, Bruce

  • Anonymous
    August 17, 2008
    Hi Bruce, Great question. One of the reasons we have structured the animating control like this is because it was initially designed on an earlier version of Silverlight. In that version, there was no support to animate a UserControl's properties - i.e. width, height etc from the parent container. This is why we used a timer to update the actual controls properties. The other reason is because during the animation we wanted regular call backs so that we could update content within the controls. Beta 2 now fully supports animatiing user controls, and Storyboard actually has a static SetTarget method, so you controls dont even require a name. Perhaps I could do a follow up post to this one, demonstrator how to do this on Beta 2, with 'better practice'. Great feedback, thanks, Martin

  • Anonymous
    August 17, 2008
    Ah, excellent. Now I understand. Yes, I've just started developing with Silverlight on Beta 2, with SetTarget being available, so you're use of SetTargetName now makes more sense. I often forget that the Silverlight world didn't begin with Beta 2 as I have. I did manage to create a class with several extension methods for several different animations, with the ability to animate any FrameworkElement as well as mix/match/combine the animations as needed. I'll have to add a callback, as you mentioned, but it should be a decent animation framework that prevents any specific need for inheritance. I'd be glad to send you a sample project (based on yours) if you're interested. Thanks much for the explanation. Looking forward to Part 2! Best, Bruce BTW-I LOVE the MSCUI demo. It's the best use of Silverlight that I've seen yet. Best for me at least, since I happen to be in the medical research software industry and love seeing great examples of how software can really improve health care and research. Kudos!

  • Anonymous
    August 18, 2008
    Hi Bruce, Thanks for your kind words on the demonstrator. We have a new release coming very soon with some cool new features, so watch this space. Would love to see your sample. You have inspired me to produce an updated version, and having never worked with extension methods before, this could be a good way for me to learn. Feel free to contact me at martin.grayson@microsoft.com, and I will check out your sample. Thanks, Martin

  • Anonymous
    November 17, 2008
    Hi Martin, Can you please tell me how to debug your attached sample project when I have Silverlight 2 RTW installed? As far as I am aware this project is built in an earlier version and I am not sure if rolling back to the previous version is the only option? Thanks in adv, Justin

  • Anonymous
    November 17, 2008
    The comment has been removed

  • Anonymous
    August 15, 2014
    Visit www.lucky-stores.com . Develop a website using osclass an opensource cms for classified websites