好幾個月沒寫 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 類型,也進行動畫,這里就留給各位看官發揮想象了。


