Blog of me, Chris Idzerda, containing my thoughts, comments, and questions. RSS Feed


Animating a GridLength

WPF provides several classes for animating many different types (e.g. ColorAnimation and Rotation3DAnimation) but none for animating a GridLength structure. I wanted such a class recently to create an animated navigation panel. There are a few MSDN forum posts asking for much the same functionality and providing some solutions, much like what I present here. Mine differs in that I followed the recommended approach to creating such a class by spreading its functionality across two classes, GridLengthAnimationBase and GridLengthAnimation, the latter inheriting the former. This split facilitates creating additional derivations, such as GridLengthAnimationUsingKeyFrames.

    public abstract class GridLengthAnimationBase : AnimationTimeline
    {
        public override sealed object GetCurrentValue(object defaultOriginValue, object defaultDestinationValue, AnimationClock animationClock)
        {
            if(!(defaultOriginValue is GridLength))
                throw new ArgumentException("Parameter must be a GridLength.", "defaultOriginValue");
            if(!(defaultDestinationValue is GridLength))
                throw new ArgumentException("Parameter must be a GridLength.", "defaultDestinationValue");
            return GetCurrentValueCore((GridLength)defaultOriginValue, (GridLength)defaultDestinationValue, animationClock);
        }
 
        protected abstract GridLength GetCurrentValueCore(GridLength defaultOriginValue, GridLength defaultDestinationValue, AnimationClock animationClock);
 
        public override sealed Type TargetPropertyType
        {
            get { return typeof(GridLength); }
        }
    }
 
    public class GridLengthAnimation : GridLengthAnimationBase
    {
        public static readonly DependencyProperty ByProperty= DependencyProperty.Register("By",
            typeof(double?), typeof(GridLengthAnimation));
        public static readonly DependencyProperty FromProperty= DependencyProperty.Register("From",
            typeof(double?), typeof(GridLengthAnimation));
        public static readonly DependencyProperty ToProperty= DependencyProperty.Register("To",
            typeof(double?), typeof(GridLengthAnimation));
 
        protected override Freezable CreateInstanceCore()
        {
            return new GridLengthAnimation();
        }
 
        protected override GridLength GetCurrentValueCore(GridLength defaultOriginValue, GridLength defaultDestinationValue, AnimationClock animationClock)
        {
            double fromValue= From.HasValue ? From.Value : defaultOriginValue.Value, toValue;
            if(To.HasValue)
                toValue= To.Value;
            else if(By.HasValue)
                toValue= fromValue + By.Value;
            else
                throw new ApplicationException("Specify either To or By in a GridLengthAnimation.");
            return new GridLength(fromValue + animationClock.CurrentProgress.Value * (toValue - fromValue), GridUnitType.Star);
        }
 
        public double? By
        {
            get { return (double?)this.GetValue(ByProperty); }
            set { this.SetValue(ByProperty, value); }
        }
 
        public double? From
        {
            get { return (double?)this.GetValue(FromProperty); }
            set { this.SetValue(FromProperty, value); }
        }
 
        public double? To
        {
            get { return (double?)this.GetValue(ToProperty); }
            set { this.SetValue(ToProperty, value); }
        }
    }

 

Note that my implementation assumes "Star" sizing (highlighted above) which is why the dependency properties are of type Nullable<double> instead of type GridLength. It is possible to modify it to support Pixel sizing as well but such an implementation must guard against animating between incompatible GridLength instances, such as from a Pixel-sized GridLength to a Star-sized GridLength. Besides, I didn't need any more than this for my animated navigation panel user control, shown here.

    <Grid x:Name="LayoutRoot">
        <Grid.RowDefinitions>
            <RowDefinition Height="21.96" />
            <RowDefinition Height="0*" />
            <RowDefinition Height="21.96" />
            <RowDefinition Height="*" />
            <RowDefinition Height="21.96" />
            <RowDefinition Height="0*" />
        </Grid.RowDefinitions>
        <Button Content="Button 1" x:Name="button1" Click="button_Click"/>
        <Grid Grid.Row="1">
            <TextBlock Text="Content 1"/>
        </Grid>
        <Button Content="Button 2" Grid.Row="2" x:Name="button2" Click="button_Click"/>
        <Grid Grid.Row="3">
            <TextBlock Text="Content 2"/>
        </Grid>
        <Button Content="Button 3" Grid.Row="4" x:Name="button3" Click="button_Click"/>
        <Grid Grid.Row="5">
            <TextBlock Text="Content 3"/>
        </Grid>
    </Grid>
 
    public partial class NavigationPanel
    {
        public NavigationPanel()
        {
            this.InitializeComponent();
        }
 
        private void ExpandRow(int rowIndex)
        {
            RowDefinitionCollection rowDefs= LayoutRoot.RowDefinitions;
            for(int i= 1; i < rowDefs.Count; i += 2)
            {
                if((rowDefs[i].Height.Value == 0) == (i == rowIndex))
                {
                    GridLengthAnimation gla= new GridLengthAnimation();
                    gla.To= i == rowIndex ? 1 : 0;
                    gla.Duration= new Duration(TimeSpan.FromSeconds(.125));
                    rowDefs[i].BeginAnimation(RowDefinition.HeightProperty, gla);
                }
            }
        }
 
        private void button_Click(object sender, RoutedEventArgs e)
        {
            DependencyObject dob= (DependencyObject)sender;
            int rowIndex= (int)dob.GetValue(Grid.RowProperty);
            this.ExpandRow(rowIndex + 1);
        }
    }

 

It turns out I never needed this code but I provide it now for your edification.

 
Posted by Chris Idzerda | 0 Comments | Trackback Url | Bookmark with:        
Tags:

Links to this Post

Comments

Name:
URL:
Email:
Comments:

CAPTCHA Image Validation