WPF:从代码中实现TranslateTransform动画

21
我有一个WPF画布,我正在从代码动态创建对象。这些对象通过设置RenderTransform属性进行转换,并且需要在其中一个转换上应用动画。目前,我无法使任何转换的属性动画化(尽管不会引发任何异常并且动画似乎在运行 - 完成事件被触发)。
此外,如果动画系统受到压力,有时Storyboard.Completed事件永远不会被触发。
我遇到的所有示例都是从XAML中对转换进行动画处理。MSDN文档建议必须设置转换的x:Name属性才能进行动画处理,但我没有找到一种可行的方法来从代码中设置它。
有什么想法吗?
以下是完整的代码清单,可重现该问题:
using System;
using System.Diagnostics;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;

namespace AnimationCompletedTest {
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window {

        Canvas panel;
        public MainWindow() {
            InitializeComponent();
            MouseDown += DoDynamicAnimation;

            Content = panel = new Canvas();
        }

        void DoDynamicAnimation(object sender, MouseButtonEventArgs args) {

            for (int i = 0; i < 12; ++i) {
                var e = new Ellipse {
                    Width = 16,
                    Height = 16,
                    Fill = SystemColors.HighlightBrush
                };
                Canvas.SetLeft(e, Mouse.GetPosition(this).X);
                Canvas.SetTop(e, Mouse.GetPosition(this).Y);

                var tg = new TransformGroup();
                var translation = new TranslateTransform(30, 0);
                tg.Children.Add(translation);
                tg.Children.Add(new RotateTransform(i * 30));
                e.RenderTransform = tg;

                panel.Children.Add(e);

                var s = new Storyboard();
                Storyboard.SetTarget(s, translation);
                Storyboard.SetTargetProperty(s, new PropertyPath(TranslateTransform.XProperty));

                s.Children.Add(
                    new DoubleAnimation(3, 100, new Duration(new TimeSpan(0, 0, 0, 1, 0))) {
                        EasingFunction = new PowerEase {EasingMode = EasingMode.EaseOut}
                    });

                s.Completed += 
                    (sndr, evtArgs) => {
                        Debug.WriteLine("Animation {0} completed {1}", s.GetHashCode(), Stopwatch.GetTimestamp());
                        panel.Children.Remove(e);
                    };

                Debug.WriteLine("Animation {0} started {1}", s.GetHashCode(), Stopwatch.GetTimestamp());

                s.Begin();
            }
        }

        [STAThread]
        public static void Main() {
            var app = new Application();
            app.Run(new MainWindow());
        }
    }
}
4个回答

36

不要使用Storyboard:

var T = new TranslateTransform(40, 0);
Duration duration = new Duration(new TimeSpan(0, 0, 0, 1, 0));
DoubleAnimation anim = new DoubleAnimation(30, duration);
T.BeginAnimation(TranslateTransform.YProperty, anim);

(语法小修复)


3
抱歉,请问您在哪里指定要对哪个元素进行动画? - javirs
1
@javirs 将 "var T" 和 "T" 替换为您想要动画的变换,例如 "myEllipse.RenderTransform"(或在 OP 的代码中使用 "e.RenderTransform")。 - Arda
我明白了 :) 感谢您提供的详细信息。 - javirs

13

经过一番谷歌搜索,看起来我自己解决了这个问题。非常感谢MSDN文档Antares19在MSDN论坛上的帖子

总结一下:

  • 对于可冻结对象(如TranslateTransform)可以被Storyboard定位,必须要有一个已注册的名称。可以通过调用FrameworkElement.RegisterName(..)来完成。

  • 我将Storyboard对象添加到同一个Framework元素的ResourceDictionary中,该元素中的TranslateTransform已经被注册了。可以通过调用ResourceDictionary.Add(..)来完成。

这里是更新后的代码,现在可以很好地执行动画,并注册/取消注册添加的资源:

using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;

namespace AnimationCompletedTest {

    public partial class MainWindow : Window {

        Canvas panel;
        public MainWindow() {
            InitializeComponent();
            MouseDown += DoDynamicAnimation;

            Content = panel = new Canvas();
        }

        void DoDynamicAnimation(object sender, MouseButtonEventArgs args) {
            for (int i = 0; i < 12; ++i) {
                var e = new Ellipse { Width = 16, Height = 16, Fill = SystemColors.HighlightBrush };
                Canvas.SetLeft(e, Mouse.GetPosition(this).X);
                Canvas.SetTop(e, Mouse.GetPosition(this).Y);

                var tg = new TransformGroup();
                var translation = new TranslateTransform(30, 0);
                var translationName = "myTranslation" + translation.GetHashCode();
                RegisterName(translationName, translation);
                tg.Children.Add(translation);
                tg.Children.Add(new RotateTransform(i * 30));
                e.RenderTransform = tg;

                panel.Children.Add(e);

                var anim = new DoubleAnimation(3, 100, new Duration(new TimeSpan(0, 0, 0, 1, 0))) {
                    EasingFunction = new PowerEase { EasingMode = EasingMode.EaseOut }
                };

                var s = new Storyboard();
                Storyboard.SetTargetName(s, translationName);
                Storyboard.SetTargetProperty(s, new PropertyPath(TranslateTransform.YProperty));
                var storyboardName = "s" + s.GetHashCode();
                Resources.Add(storyboardName, s);

                s.Children.Add(anim);

                s.Completed +=
                    (sndr, evtArgs) => {
                        panel.Children.Remove(e);
                        Resources.Remove(storyboardName);
                        UnregisterName(translationName);
                    };
                s.Begin();
            }
        }

        [STAThread]
        public static void Main() {
            var app = new Application();
            app.Run(new MainWindow());
        }
    }
}

4

我有一个使用 XAML / C# 组合的解决方案。在您的 XAML 文件中定义:

<UserControl.RenderTransform>
    <TranslateTransform x:Name="panelTrans" Y="0"></TranslateTransform>
</UserControl.RenderTransform>

这将使您能够在C#代码中执行以下操作:
        Storyboard.SetTargetName(mFlyInDA, "panelTrans");
        Storyboard.SetTargetProperty(mFlyInDA, new PropertyPath("Y"));

其余的部分照常进行。创建一个DoubleAnimation,设置它的属性,将其作为子元素添加到您的Storyboard中,在故事板上调用Begin函数即可。

2
如果您要对目标FrameworkElement进行动画处理,则无需注册转换或将Storyboard添加到资源集合中。您可以使用Blend生成PropertyPath语法。
frameworkElement.RenderTransform = new TransformGroup
        {
            Children =
            {
                new ScaleTransform(),
                new SkewTransform(),
                new RotateTransform(),
                translate
            }
        };
translate.X = 100.0;
var animation = new DoubleAnimation(0.0, TimeSpan.FromSeconds(1));
var sb = new Storyboard();            
sb.Children.Add(animation);
Storyboard.SetTarget(animation, frameworkElement);
Storyboard.SetTargetProperty(animation, new PropertyPath("(UIElement.RenderTransform).(TransformGroup.Children)[3].(TranslateTransform.X)"));
sb.Begin();

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接