[WPF] GridLengthAnimation - 讓Grid的Row和Column大小改變也能套用動畫

  • 9486
  • 0
  • C#
  • 2013-07-14

在執行期動態的改變Grid容器中某個Row或是Column大小的功能應該是個蠻常見的情況,但是有時候我們希望由程式來調整Row的高度或是Column的寬度時,可以很輕鬆的透過程式來解決,但是,如果在改變的時候,要套用動畫呢??


在執行期動態的改變Grid容器中某個Row或是Column大小的功能應該是個蠻常見的情況,但是有時候我們希望由程式來調整Row的高度或是Column的寬度時,可以很輕鬆的透過程式來解決,但是,如果在改變的時候,要套用動畫呢??
Grid的分割單位使用的是GridLength,並不是Double,所以沒辦法直接套用DoubleAnimation,這時候怎麼辦呢?還好WPF可以讓我們繼承AnimationTimeline來實作自己的新動畫,所以我們就自己來打造一個GridLengthAnimation的新動畫類別吧!!

AnimationTimeline中有幾個重要的Property是和動畫息息相關的:
1. From屬性:GridLength的初始值
2. To屬性:GridLength的結束值
3. EasingFunction屬性:動畫都做了,不能套EasingFunction怎麼行呢!? (注意:IEasingFunction是.Net Framework4.0新增的介面)
4. TargetPropertyType:當然是GridLength囉
5. CurrentValue:會回傳0~1之間的double值,用來控制動畫撥放的動線。

 

有了以上的觀念,我們就可以動手啦!!剛剛前面說了,有三個屬性要做,所以我們必需註冊三個DependencyProperty,另外要overrideCreateInstanceCore()GetCurrentValue()這兩個Function和TargetPropertyType這個Property。

 

廢話不多說,就直接把原始碼Po出來,請各位服用吧!!

GridLengthAnimation.cs

using System;
using System.Windows;
using System.Windows.Media.Animation;
 
public class GridLengthAnimation : AnimationTimeline
{
    public static readonly DependencyProperty FromProperty;
    public static readonly DependencyProperty ToProperty;
    public static readonly DependencyProperty EasingFunctionProperty;
 
    static GridLengthAnimation()
    {
        FromProperty = DependencyProperty.Register( "From" , typeof( GridLength ) , typeof( GridLengthAnimation ) );
        ToProperty = DependencyProperty.Register( "To" , typeof( GridLength ) , typeof( GridLengthAnimation ) );
        EasingFunctionProperty = DependencyProperty.Register( "EasingFunction" , typeof( IEasingFunction ) , typeof( GridLengthAnimation ) );
    }
 
    protected override Freezable CreateInstanceCore()
    {
        return new GridLengthAnimation();
    }
 
    public override Type TargetPropertyType
    {
        get { return typeof( GridLength ); }
    }
 
    public IEasingFunction EasingFunction
    {
        get
        {
            return ( IEasingFunction ) GetValue( GridLengthAnimation.EasingFunctionProperty );
        }
        set
        {
            SetValue( GridLengthAnimation.EasingFunctionProperty , value );
        }
 
    }
 
    public GridLength From
    {
        get
        {
            return ( GridLength ) GetValue( GridLengthAnimation.FromProperty );
        }
        set
        {
            SetValue( GridLengthAnimation.FromProperty , value );
        }
    }
 
    public GridLength To
    {
        get
        {
            return ( GridLength ) GetValue( GridLengthAnimation.ToProperty );
        }
        set
        {
            SetValue( GridLengthAnimation.ToProperty , value );
        }
    }
 
    public override object GetCurrentValue( object defaultOriginValue , object defaultDestinationValue , AnimationClock animationClock )
    {
        double fromValue = ( ( GridLength ) GetValue( GridLengthAnimation.FromProperty ) ).Value;
        double toValue = ( ( GridLength ) GetValue( GridLengthAnimation.ToProperty ) ).Value;
 
        IEasingFunction easingFunction = this.EasingFunction;
 
        double progress = ( easingFunction != null ) ? easingFunction.Ease( animationClock.CurrentProgress.Value ) : animationClock.CurrentProgress.Value;
 
        if( fromValue > toValue )
        {
            return new GridLength( ( 1 - progress ) * ( fromValue - toValue ) + toValue , this.To.IsStar ? GridUnitType.Star : GridUnitType.Pixel );
        }
        else
        {
            return new GridLength( ( progress ) * ( toValue - fromValue ) + fromValue , this.To.IsStar ? GridUnitType.Star : GridUnitType.Pixel );
        }
    }
}


至於使用方法呢?附上小小的範例給各位參考: