【UWP】对 Thickness 类型属性进行动画

好几个月没写 blog 了,一个是在忙新版的碧影壁纸,另一方面是等(观望)周年更新的 api(不过现在还是比较失望,仍然没法支持矩形以外的 Clip)。闲话少说,进入主题。

在 UWP 中,出于性能考虑,微软是不建议、不推荐对会影响布局的属性进行动画的。例如 Width 和 Height 这种,如果真的需要对这些属性进行动画的话(毕竟需求就摆在那里),可以将 Animation 的 EnableDependentAnimation 属性设置为 true 来对这些属性进行动画的。

但是,对于 Thickness 类型来说,这是行不通的,因为 UWP 中并没有 ThicknessAnimation 这种动画类型(PS:WPF 里是有这种动画类型的说)。

不过既然我标题都写了出来,那办法肯定是有的。Thickness 就是四个方向分量,也就是说,对这四个方向分量进行动画就等于对这个 Thickness 进行了动画。

还有另外一点要注意的是,Thickness 类型的四个属性并不是依赖属性。

例如:

control.Margin.Left = 10;

这一句是没有效果的。

要实现效果,只能对 Margin 属性从新赋一个值:

var margin = control.Margin;
margin.Left = 10;
control.Margin = margin;

也就是说,我们需要一个可绑定的 Margin。(我就叫它 BindableMargin)

新建一个用户控件

为什么是用户控件?因为经过我的发现,我们自定义的类的依赖属性,得有 xaml 文件才能进行动画。(不信你可以试试^-^)

然后修改 BindableMargin.xaml 如下:

<DependencyObject x:Class="AnimateThicknessDemo.BindableMargin"
                  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                  xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
                  xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
                  mc:Ignorable="d" />

相当简单的一段 xaml,设计器就无视好了。重点在 BindableMargin.xaml.cs 里,修改代码:

    public partial class BindableMargin
    {
        public static readonly DependencyProperty BottomProperty = DependencyProperty.Register(nameof(Bottom), typeof(double), typeof(BindableMargin), new PropertyMetadata(default(double), BottomChanged));

        public static readonly DependencyProperty LeftProperty = DependencyProperty.Register(nameof(Left), typeof(double), typeof(BindableMargin), new PropertyMetadata(default(double), LeftChanged));

        public static readonly DependencyProperty RightProperty = DependencyProperty.Register(nameof(Right), typeof(double), typeof(BindableMargin), new PropertyMetadata(default(double), RightChanged));

        public static readonly DependencyProperty TopProperty = DependencyProperty.Register(nameof(Top), typeof(double), typeof(BindableMargin), new PropertyMetadata(default(double), TopChanged));

        private readonly FrameworkElement _owner;

        public BindableMargin(FrameworkElement owner)
        {
            if (owner == null)
            {
                throw new ArgumentNullException(nameof(owner));
            }

            _owner = owner;
        }

        public double Bottom
        {
            get
            {
                var ownerBottom = _owner.Margin.Bottom;
                var bottom = (double)GetValue(BottomProperty);
                if (ownerBottom.Equals(bottom) == false)
                {
                    SetValue(BottomProperty, ownerBottom);
                }
                return ownerBottom;
            }
            set
            {
                SetValue(BottomProperty, value);
            }
        }

        public double Left
        {
            get
            {
                var ownerLeft = _owner.Margin.Left;
                var left = (double)GetValue(LeftProperty);
                if (ownerLeft.Equals(left) == false)
                {
                    SetValue(LeftProperty, ownerLeft);
                }
                return ownerLeft;
            }
            set
            {
                SetValue(LeftProperty, value);
            }
        }

        public double Right
        {
            get
            {
                var ownerRight = _owner.Margin.Right;
                var right = (double)GetValue(RightProperty);
                if (ownerRight.Equals(right) == false)
                {
                    SetValue(RightProperty, ownerRight);
                }
                return ownerRight;
            }
            set
            {
                SetValue(RightProperty, value);
            }
        }

        public double Top
        {
            get
            {
                var ownerTop = _owner.Margin.Top;
                var top = (double)GetValue(TopProperty);
                if (ownerTop.Equals(top) == false)
                {
                    SetValue(TopProperty, ownerTop);
                }
                return ownerTop;
            }
            set
            {
                SetValue(TopProperty, value);
            }
        }

        private static void BottomChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var obj = (BindableMargin)d;
            var value = (double)e.NewValue;

            var owner = obj._owner;
            var margin = owner.Margin;
            margin.Bottom = value;
            owner.Margin = margin;
        }

        private static void LeftChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var obj = (BindableMargin)d;
            var value = (double)e.NewValue;

            var owner = obj._owner;
            var margin = owner.Margin;
            margin.Left = value;
            owner.Margin = margin;
        }

        private static void RightChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var obj = (BindableMargin)d;
            var value = (double)e.NewValue;

            var owner = obj._owner;
            var margin = owner.Margin;
            margin.Right = value;
            owner.Margin = margin;
        }

        private static void TopChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var obj = (BindableMargin)d;
            var value = (double)e.NewValue;

            var owner = obj._owner;
            var margin = owner.Margin;
            margin.Top = value;
            owner.Margin = margin;
        }
    }

别看代码这么多,其实不复杂。构造函数传入需要动画的控件。然后四个方向的依赖属性,值发生改变时回写到控件上。

然后动画的例子代码:

BindableMargin margin = new BindableMargin(control);
DoubleAnimation animation = new DoubleAnimation();
animation.EnableDependentAnimation = true;
animation.From = 0;
animation.To = 100;
animation.Duration = TimeSpan.FromSeconds(1);
Storyboard.SetTarget(animation, margin);
Storyboard.SetTargetProperty(animation, "Left");
await storyboard.BeginAsync();// WinRTXamlToolkit 里的扩展方法。
margin.Left = 100;

另外建议在动画播放完毕后,执行一次常规的赋值操作(一般赋最终值),因为视乎机器的配置,Storyboard 会有一定程度的跳帧,在低端的机器,可能动画就完全跳过去了。

说了这么多,还是说说有啥应用吧。

这是一个类似 IT 之家的通知控件。通过动画了 Margin 的 Right 来实现的。

Demo 下载地址:AnimateThicknessDemo.zip

当然应用还有很多,例如对 Border 的圆角进行动画。通过这么一种“桥”的方式,我们可以对很多属性,并不局限于 Thickness 类型,也进行动画,这里就留给各位看官发挥想象了。

时间: 2024-10-13 20:46:25

【UWP】对 Thickness 类型属性进行动画的相关文章

MVC自定义编辑视图,DateTime类型属性显示jQuery ui的datapicker

实现的效果为:在编辑视图中,对DateTime类型的属性,显示jQuery UI的datepicker.效果如下: Student.cs public class Student    {        public int Id { get; set; }        public string Name { get; set; }        public DateTime? JoinTime { get; set; }    } HomeController: public class

Silverlight代码编写对控件的PlaneProjection.RotationY属性控制动画

Canvas c; void btnDraw_Click(object sender, RoutedEventArgs e) { Storyboard story = new Storyboard(); DoubleAnimation yAnimation = new DoubleAnimation(); yAnimation.From = 0.5; yAnimation.To = 100; yAnimation.Duration = new Duration(TimeSpan.FromSeco

1.4.2 solr字段类型--(1.4.2.1)字段类型定义和字段类型属性

1.4.2 solr字段类型 (1.4.2.1) 字段类型定义和字段类型属性. (1.4.2.2) solr附带的字段类型 (1.4.2.3) 使用货币和汇率 (1.4.2.4) 使用Dates(日期) (1.4.2.5) 使用枚举字段 (1.4.2.6) 使用外部文件和程序 (1.4.2.7) 字段属性使用案例 字段类型定义和字段类型属性 字段类型元素fieldType包含4个信息的类型:name,class-实现类的名称,analyzer-用于字段类型的分析,字段属性Field. schem

Swift编程语言学习11—— 枚举全局变量、局部变量与类型属性

全局变量和局部变量 计算属性和属性监视器所描写叙述的模式也能够用于全局变量和局部变量,全局变量是在函数.方法.闭包或不论什么类型之外定义的变量,局部变量是在函数.方法或闭包内部定义的变量. 前面章节提到的全局或局部变量都属于存储型变量,跟存储属性类似,它提供特定类型的存储空间,并同意读取和写入. 另外,在全局或局部范围都能够定义计算型变量和为存储型变量定义监视器,计算型变量跟计算属性一样,返回一个计算的值而不是存储值,声明格式也全然一样. 注意: 全局的常量或变量都是延迟计算的,跟延迟存储属性相

Swift - 类型属性(类静态属性)和类方法(类静态方法)

1,结构体struct和枚举enum的静态属性,静态方法使用static关键字 1 2 3 4 5 6 7 8 9 10 struct Account {      var amount : Double = 0.0                 //账户金额      var owner : String = ""                   //账户名      static var interestRate : Double = 0.668  //利率     stat

Swift中的类型属性(静态变量)

http://blog.haohtml.com/archives/15098 Swift中的类型属性(静态变量) Posted on 2014/06/13 类型属性语法 在 C 或 Objective-C 中,静态常量和静态变量的定义是通过特定类型加上global关键字.在 Swift 编程语言中,类型属性是作为类型定义的一部分写在类型最外层的花括号内,因此它的作用范围也就在类型支持的范围内. 使用关键字static来定义值类型的类型属性,关键字class来为类(class)定义类型属性.下面的

Swift 的全局变量、局部变量与类型属性

全局变量和局部变量 计算属性和属性监视器所描述的模式也可以用于全局变量和局部变量,全局变量是在函数.方法.闭包或任何类型之外定义的变量,局部变量是在函数.方法或闭包内部定义的变量. 前面章节提到的全局或局部变量都属于存储型变量,跟存储属性类似,它提供特定类型的存储空间,并允许读取和写入. 另外,在全局或局部范围都可以定义计算型变量和为存储型变量定义监视器,计算型变量跟计算属性一样,返回一个计算的值而不是存储值,声明格式也完全一样. 注意: 全局的常量或变量都是延迟计算的,跟延迟存储属性相似,不同

Java框架spring Boot学习笔记(九):注入对象类型属性

使用set方法注入对象属性 编写UserDao.java文件 1 package com.example.spring; 2 3 public class UserDao { 4 public void print(){ 5 System.out.println("Dao print."); 6 } 7 } 编写UserService.java文件 1 package com.example.spring; 2 3 public class UserService { 4 //1.定义

Spring根据XML配置文件注入对象类型属性

这里有dao.service和Servlet三个地方 通过配过文件xml生成对象,并注入对象类型的属性,降低耦合 dao文件代码: package com.swift; public class DaoUser { public void fun() { System.out.println("I'm dao's fun()...................."); } } service文件代码:(提供setter方法,xml文件可通过这种方法配置) package com.sw