本文告訴大家,在後臺程式碼,對 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 的屬性做動畫將會失效。好在官方文件裡面有說到這點
本文程式碼還請到 github 或 gitee 上閱讀程式碼
可以通過如下方式獲取本文的原始碼,先建立一個空資料夾,接著使用命令列 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 資料夾