GridLength动画使用关键帧?

时间:2012-01-09 08:40:33

标签: wpf

我想知道是否有任何类可以使用GridLength设置KeyFrames值的动画?我看过以下网站,但没有一个网站使用KeyFrames

有什么建议吗?

2 个答案:

答案 0 :(得分:1)

创建附加行为并为其设置动画。

当然,GridLength显然不是数字类型,因此不清楚它是如何动画的。为了证明我可以创建一个附加的行为,如:

public class AnimatableProperties
    {
        public static readonly DependencyProperty WidthProperty =
            DependencyProperty.RegisterAttached("Width",
            typeof(double),
            typeof(DependencyObject),
            new PropertyMetadata(-1, (o, e) => 
            {
                AnimatableProperties.OnWidthChanged((Grid)o, (double)e.NewValue);
            }));

        public static void SetWidth(DependencyObject o,
            double e)
        {
            o.SetValue(AnimatableProperties.WidthProperty, e);
        }

        public static double GetWidth(DependencyObject o)
        {
            return (double)o.GetValue(AnimatableProperties.WidthProperty);
        }

        private static void OnWidthChanged(DependencyObject target,
            double e)
        {
            target.SetValue(Grid.WidthProperty, new GridLength(e));
        }
    }

这将重新生成网格宽度作为double类型的数字属性。有了它,你可以自由地为它设置动画。

P.S。显然,使用Grid的宽度并没有太大意义,因为它已经加倍了。任何其他基于GridLength的属性都可以根据上面的示例使用双包装器进行编程,然后通过该包装器进行动画处理。

答案 1 :(得分:0)

这是相当直接的,但您需要使用适配器,因为您不能使用DoubleAnimator直接为ColumnDefinition类上的Width设置动画,因为ColumnDefinition不是double。这是我的代码:

public class ColumnDefinitionDoubleAnimationAdapter : Control
{
    #region Dependency Properties
    public static readonly DependencyProperty WidthProperty = DependencyProperty.Register(nameof(Width), typeof(double), typeof(ColumnDefinitionDoubleAnimationAdapter), new PropertyMetadata((double)0, WidthChanged));

    private static void WidthChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var columnDefinitionDoubleAnimationAdapter = (ColumnDefinitionDoubleAnimationAdapter)d;
        columnDefinitionDoubleAnimationAdapter.Width = (double)e.NewValue;
    }
    #endregion

    #region Fields
    private ColumnDefinition _ColumnDefinition;
    #endregion

    #region Constructor
    public ColumnDefinitionDoubleAnimationAdapter(ColumnDefinition columnDefinition)
    {
        _ColumnDefinition = columnDefinition;
    }
    #endregion

    #region Public Properties
    public double Width
    {
        get
        {
            return (double)GetValue(WidthProperty);
        }
        set
        {
            SetValue(WidthProperty, value);
            _ColumnDefinition.Width = new GridLength(value);
        }
    }
    #endregion
}

不幸的是,上面的效率非常低,因为它会一次又一次地创建一个GridLength,因为ColumnDefinition.Width.Value应该是只读的。

这是一种动画制作方法。使用基于任务的异步非常重要,否则故事板将超出范围并导致不良行为。无论如何这是一个好习惯,所以如果你需要,你可以等待动画:

    public async static Task AnimateColumnWidth(ColumnDefinition columnDefinition, double from, double to, TimeSpan duration, IEasingFunction ease)
    {
        var taskCompletionSource = new TaskCompletionSource<bool>();

        var storyboard = new Storyboard();

        var animation = new DoubleAnimation();
        animation.EasingFunction = ease;
        animation.Duration = new Duration(duration);
        storyboard.Children.Add(animation);
        animation.From = from;
        animation.To = to;

        var columnDefinitionDoubleAnimationAdapter = new ColumnDefinitionDoubleAnimationAdapter(columnDefinition);

        Storyboard.SetTarget(animation, columnDefinitionDoubleAnimationAdapter);
        Storyboard.SetTargetProperty(animation, new PropertyPath(ColumnDefinitionDoubleAnimationAdapter.WidthProperty));

        storyboard.Completed += (a, b) =>
        {
            taskCompletionSource.SetResult(true);
        };

        storyboard.Begin();

        await taskCompletionSource.Task;
    }

一个示例用法:

    private async void TheMenu_HamburgerToggled(object sender, EventArgs e)
    {
        TheMenu.IsOpen = !TheMenu.IsOpen;

        var twoSeconds = TimeSpan.FromMilliseconds(120);
        var ease = new CircleEase { EasingMode = TheMenu.IsOpen ? EasingMode.EaseIn : EasingMode.EaseOut };
        if (TheMenu.IsOpen)
        {
            await UIUtilities.AnimateColumnWidth(MenuColumn, 40, 320, twoSeconds, ease);
        }
        else
        {
            await UIUtilities.AnimateColumnWidth(MenuColumn, 320, 40, twoSeconds, ease);
        }
    }