欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

WPF 后台代码做 TranslateTransform 的动画

程序员文章站 2022-05-18 09:17:50
本文告诉大家,在后台代码,对 TranslateTransform 做动画的方法 ......

本文告诉大家,在后台代码,对 translatetransform 做动画的方法

今天小伙伴问我一个问题,说为什么相同的代码,如果设置到按钮上,是可以让按钮的某个属性变更,但是如果设置给 translatetransform 的 x 或 y 就不会有任何值变更

在 wpf 中,通过 官方文档 里面的描述,对于 freezable 类型的对象,如 solidcolorbrush 和 rotatetransform 和 gradientstop 等类型,都是不支持直接的动画,也就是如以下代码是不能触发动画

假定有 xaml 界面如下,期望在点击按钮时,修改按钮的 translatetransform 做动画

  <grid>
    <button x:name="button" horizontalalignment="center" verticalalignment="center" content="按钮" click="button_onclick">
      <button.rendertransform>
        <translatetransform x:name="buttontranslatetransform"></translatetransform>
      </button.rendertransform>
    </button>
  </grid>

如果直接对使用 storyboard 的 settarget 方法给对象设置 doubleanimation 将会是无效的,也就是说如以下的代码做的 translatetransform 动画是无效的,没有反应的

        private void button_onclick(object sender, routedeventargs e)
        {
            var storyboard = new storyboard();

            var doubleanimation = new doubleanimation();
            storyboard.settarget(doubleanimation, buttontranslatetransform);
            storyboard.settargetproperty(doubleanimation, new propertypath(translatetransform.xproperty));

            doubleanimation.to = 100;
            doubleanimation.duration = new duration(timespan.fromseconds(2));

            storyboard.children.add(doubleanimation);
            storyboard.begin();
        }

如果想要给 freezable 类型的对象做动画,可以通过间接的方法,也就是通过 freezable 类型的对象所在的元素,使用点的方式写出来具体的代码

        private void button_onclick(object sender, routedeventargs e)
        {
            var storyboard = new storyboard();

            var doubleanimation = new doubleanimation();
            storyboard.settarget(doubleanimation, button);
            storyboard.settargetproperty(doubleanimation, new propertypath("(uielement.rendertransform).(translatetransform.x)"));

            doubleanimation.to = 100;
            doubleanimation.duration = new duration(timespan.fromseconds(2));

            storyboard.children.add(doubleanimation);
            storyboard.begin();
        }

写法就是通过某个元素的某个属性加上某个类型的某个属性。如上面代码使用的是 uielement 的 rendertransform 属性,这个属性的值的类型是 translatetransform 类型,设置这个类型的 x 属性

上面的 propertypath 有可以换成如下方式写

            var propertychain = new object[]
            {
                uielement.rendertransformproperty,
                translatetransform.xproperty
            };

            storyboard.settargetproperty(doubleanimation, new propertypath("(0).(1)", propertychain));

我更推荐使用这个写法,因为这样就不会写错命名

而如果只是为了修改 translatetransform 的 x 属性,最简单的写法就是通过 beginanimation 的方式,如下面代码

        private void button_onclick(object sender, routedeventargs e)
        {
            buttontranslatetransform.beginanimation(translatetransform.xproperty, new doubleanimation()
            {
                to = 100,
                duration = new duration(timespan.fromseconds(1))
            });
        }

以上代码可以看到很清真

这里的 duration 其实可以通过 timespan 转换,而不需要创建 duration 对象。然而在 wpf 依然定义 duration 类的原因是为了支持 duration.automatic 和 duration.forever 特殊的定义

如果是需要有多个属性开始做动画,不想使用 beginanimation 的方式,可以通过在后台代码用 settargetname 的方法指定,如下面代码

        private void button_onclick(object sender, routedeventargs e)
        {
            var storyboard = new storyboard();

            var doubleanimation = new doubleanimation();
            storyboard.settargetname(doubleanimation, nameof(buttontranslatetransform));
            storyboard.settargetproperty(doubleanimation, new propertypath(translatetransform.xproperty));

            doubleanimation.to = 100;
            doubleanimation.duration = new duration(timespan.fromseconds(2));

            var storyboardname = "s" + storyboard.gethashcode();
            // 加入到字典,让 storyboard 和 buttontranslatetransform 在相同的一个 namescope 里
            resources.add(storyboardname, storyboard);

            storyboard.children.add(doubleanimation);
            storyboard.begin();
        }

在后台代码做动画,如果使用 settargetname 就需要让 storyboard 和对应的元素在相同的一个 namescope 里,不然将会提示 system.invalidoperationexception 不存在可解析名称“xx”的适用名称领域,如下面代码

system.invalidoperationexception:“不存在可解析名称“buttontranslatetransform”的适用名称领域。”

上面代码通过将动画加入到资源字典的方式,让动画和元素在相同的 namescope 而让动画能找到元素。但是上面代码将会在资源字典加入一个 storyboard 而没有释放,如果在你的实际代码,我推荐在动画完成之后,删除资源字典的动画

我特别翻了 wpf 编程宝典,发现宝典里面没有这部分知识,也就是没有告诉大家为什么直接给 translatetransform 的属性做动画将会失效。好在官方文档里面有说到这点

本文代码还请到 githubgitee 上阅读代码

可以通过如下方式获取本文的源代码,先创建一个空文件夹,接着使用命令行 cd 命令进入此空文件夹,在命令行里面输入以下代码,即可获取到本文的代码

git init
git remote add origin https://gitee.com/lindexi/lindexi_gd.git
git pull origin 78f63c1c076065d1891559f5af2cb29f10a39f8b

以上使用的是 gitee 的源,如果 gitee 不能访问,请替换为 github 的源

git remote remove origin
git remote add origin https://github.com/lindexi/lindexi_gd.git

获取代码之后,进入 kayceefiwhearhaijanihukere 文件夹

storyboards overview - wpf .net framework