WPF 後臺代碼做 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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章