Improving AnimatedPanel in SilverlightFX  

Sunday, December 07 2008

After really taking a look at SilverlightFX I realized that Nikhil's implementation of an AnimatedPanel is clearly superior to other implementations out there (See here for an example) . There were two notable features of his implementation that led me to this conclusion. The first is that it uses the robust procedural animation system that comes with SilverlightFX (aka System.Windows.Media.Glitz). The second is that it does animated layout atomically (from VStackPanel.cs in SilverlightFX):


BeginArrange();
foreach (UIElement element in Children) {
    if (element.Visibility != Visibility.Collapsed) {
        childRect.Y += previousChildHeight;
        if (firstChild == false) {
            childRect.Y += childSpacing;
        }

        previousChildHeight = childRect.Height = element.DesiredSize.Height;

        switch (alignment) {
            case HorizontalAlignment.Stretch:
                childRect.Width = Math.Max(finalSize.Width, element.DesiredSize.Width);
                break;
            case HorizontalAlignment.Left:
                childRect.Width = Math.Min(finalSize.Width, element.DesiredSize.Width);
                break;
            case HorizontalAlignment.Right:
                childRect.Width = Math.Min(finalSize.Width, element.DesiredSize.Width);
                childRect.X = finalSize.Height - childRect.Height;
                break;
            case HorizontalAlignment.Center:
                childRect.Width = Math.Min(finalSize.Width, element.DesiredSize.Width);
                childRect.X = (finalSize.Width - childRect.Width) / 2;
                break;
        }

        ArrangeElement(element, childRect);

        firstChild = false;
    }
}
EndArrange();

All that complicated layout logic gets sandwiched between BeginArrange(); and EndArrange(). These two methods are responsible for the atomic behavior (from AnimatedPanel.cs):


protected void BeginArrange()
{
    if (_layoutAnimation != null)
    {
        if (_layoutAnimation.IsPlaying)
        {
            _layoutAnimation.Stop(ProceduralAnimationStopState.Abort);
        }
        _layoutAnimation = null;
    }

    _useAnimation = UseAnimatedLayout && _initialLayoutCompleted;
    _animations = new List<ProceduralAnimation>();

    if (_useAnimation)
    {
        _easingFunction = GetEasingFunction();
        _duration = Duration;
    }
}

protected void EndArrange()
{
    if ((_animations != null) && (_animations.Count != 0))
    {
        _layoutAnimation = new ProceduralAnimationSet(_animations.ToArray());
        _layoutAnimation.Play(this);
    }

    _animations = null;
    _initialLayoutCompleted = true;
}

As you can see, BeginArrange checks to see if any procedural animations are running and stops them. Then a new list of animations for the panel's elements is created. The arrange of any panel queues up animations into this list by calling ArrangeElement. EndArrange simply fires off the list of animations so they all start at once. It may not seem like a huge leap in style or implementation, but it's a great base class for any animating panel you're planning on writing. Animation easing functions also come prepackaged with AnimatingPanel, which can be a pain to write from scratch.

I was recently writing an animating panel and I realized that there was one small feature missing. The ability to set an initial Rect for the animation to begin from. If you say wanted a slide down from top animation, then you would have to specify that Rect explictly because it is outside of the bounds of the panel. This was easily accomplished using this method that I added:


protected void ArrangeElement(UIElement element, Rect initialRect, Rect finalRect)
{
    ArrangeElement(element, initialRect, finalRect, true);
}

For slide in or slide out animations, you can simply specify the target Rect outside of the panel. You can download my implementation from my fork of SilverlightFX on GitHub. Enjoy.


  • Posted by Charlie Robbins

Post a comment


(required, but not displayed)

(optional)