本文告訴大家,在後臺代碼,對 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 的屬性做動畫將會失效。好在官方文檔裏面有說到這點
可以通過如下方式獲取本文的源代碼,先創建一個空文件夾,接着使用命令行 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 文件夾