单独的动画工作,故事板没有。为什么呢?

时间:2016-07-23 02:46:09

标签: c# wpf animation storyboard gradient

编辑1:为了满足“Complete, Minimal And Verifiable”示例要求

TL:DR; 故事板根本没有动画效果。为什么呢?

我正在尝试创建一个故事板,它将为渐变中所有渐变色块的偏移设置动画,将它们从左向右移动。

我确定这只是一个愚蠢的语法或参数错误或某些地方,但我无法找到它。

这是XAML:

<Window
    x:Class="GradientShifting.MainWindow"
    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"
    xmlns:local="clr-namespace:GradientShiftDerping"
    mc:Ignorable="d" Title="MainWindow" Height="350" Width="525"
    AllowsTransparency="True" WindowStyle="None">
    <Window.Background>
        <LinearGradientBrush EndPoint="1,1" StartPoint="0,0">
            <GradientStop Color="Black" Offset="0"/>
            <GradientStop Color="White" Offset="1"/>
        </LinearGradientBrush>
    </Window.Background>
</Window>

这是背后的代码:

using System;
using System.Linq;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Animation;

namespace GradientShifting {
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window {
        private Storyboard _sbGradientShifter = new Storyboard( );          
        public MainWindow( ) {
            InitializeComponent( );
            this.Loaded += new RoutedEventHandler(
                ( S, E ) => this.SetupGradientShift( ) );
        }

    private void SetupGradientShift( ){
        GradientBrush BackBrush = this.Background as GradientBrush;
        if ( BackBrush != null ) {
            /* Ordering by offset is important because
               the last color in the gradient requires
               special consideration. */
            DoubleAnimationUsingKeyFrames DAUKF;
            GradientStopCollection GSC = new GradientStopCollection(
                BackBrush.GradientStops.OrderBy( GS => GS.Offset ) );
            foreach( GradientStop GS in GSC ){
                DAUKF = new DoubleAnimationUsingKeyFrames( ) {
                    KeyFrames = new DoubleKeyFrameCollection( ){
                        new LinearDoubleKeyFrame(
                            1.0D, KeyTime.FromPercent( 1.0D )
                }, Duration = TimeSpan.FromSeconds( 3 )
            };

            //Something I am doing from here...
            this._sbGradientShifter.Children.Add( DAUKF );

            Storyboard.SetTarget( DAUKF, GS );

            Storyboard.SetTargetProperty(
                DAUKF, new PropertyPath( GradientStop.OffsetProperty ) );
        }
        this._sbGradientShifter.Begin( this ); //THIS DOES NOTHING.         
    }
}

所以,再次 - 这段代码不起作用。我已经能够通过调用GradientStop.BeginAnimation来启动故事板中包含的动画,但是Storyboard.Begin不起作用。

2 个答案:

答案 0 :(得分:1)

出于某种原因,Storyboard.SetTarget仅适用于FrameworkElementFrameworkContentElement。要做你想做的事,你可以像在“黑客”中那样自己动手制作动画(完全合理的动画方式,IMO)。

或者您可以为所有目标注册名称,例如:

foreach (var gs in gsc)
{
    var name = "GS_" + Guid.NewGuid().ToString("N");
    RegisterName(name, gs);
    Storyboard.SetTargetName(caukf, name);
}

如果您决定直接调用动画,则实际上不需要将它们保存在单独的列表中。只要在创建它们后立即在第一个循环中立即启动它们。

如果您需要更多协调,故事板非常棒,例如暂停动画,使用名称范围,高级计时或XAML动画。但在你的情况下,似乎简单的时间表就足够了。

答案 1 :(得分:1)

the other answer所述,这是WPF的无证(据我所知)限制。称之为bug。有关其他详细信息,请参阅先前的帖子,例如Storyboard targetting multiple objects, using SetTarget method, doesn't workWhy don't these animations work when I'm using a storyboard?

您可以动态生成名称,如Eli的答案中所述。其他替代方法包括在XAML中指定名称,然后在代码隐藏中引用它们,或者仅在XAML中声明整个内容。在所有情况下,您都必须使用Storyboard.TargetName属性而不是Target属性。

如果要在XAML中指定名称,可以通过两种方法在代码隐藏中使用它们:您可以明确地对名称进行硬编码,也可以根据需要查找它们。如果你只需要处理一个动画而且知道名字不会改变,那么前者是合适的。如果您想将通用算法应用于多个场景,后者将是合适的。

硬编码:

private void SetupGradientShift()
{
    string[] names = { "stop1", "stop2" };

    foreach (string name in names)
    {
        DoubleAnimationUsingKeyFrames daukf =
            new DoubleAnimationUsingKeyFrames
            {
                KeyFrames =
                    new DoubleKeyFrameCollection
                    {
                        new LinearDoubleKeyFrame(1.0, KeyTime.FromPercent(1.0))
                    },
                Duration = TimeSpan.FromSeconds(3)
            };

        this._sbGradientShifter.Children.Add(daukf);
        Storyboard.SetTargetName(daukf, name);
        Storyboard.SetTargetProperty(
            daukf, new PropertyPath(GradientStop.OffsetProperty));
    }

    this._sbGradientShifter.Begin(this);
}

在运行时查找:

private void SetupGradientShift()
{
    GradientBrush BackBrush = this.Background as GradientBrush;
    if (BackBrush != null)
    {
        INameScopeDictionary nameScope = (INameScopeDictionary)NameScope.GetNameScope(this);

        foreach (GradientStop gradientStop in BackBrush.GradientStops.OrderBy(stop => stop.Offset))
        {
            DoubleAnimationUsingKeyFrames daukf =
                new DoubleAnimationUsingKeyFrames
                {
                    KeyFrames =
                        new DoubleKeyFrameCollection
                        {
                            new LinearDoubleKeyFrame(1.0, KeyTime.FromPercent(1.0))
                        },
                    Duration = TimeSpan.FromSeconds(3)
                };

            this._sbGradientShifter.Children.Add(daukf);

            string name = nameScope.First(kvp => kvp.Value == gradientStop).Key;

            Storyboard.SetTargetName(daukf, name);
            Storyboard.SetTargetProperty(
                daukf, new PropertyPath(GradientStop.OffsetProperty));
        }

        this._sbGradientShifter.Begin(this);
    }
}

无论哪种方式,您都需要在XAML中声明名称:

<Window.Background>
  <LinearGradientBrush EndPoint="1,1" StartPoint="0,0">
    <GradientStop x:Name="stop1" Color="Black" Offset="0"/>
    <GradientStop x:Name="stop2" Color="White" Offset="1"/>
  </LinearGradientBrush>
</Window.Background>

但就个人而言,我认为在XAML中完成整个动画并将代码隐藏起来实际上会更好:

<Window x:Class="TestSO38537640AnimateCodeBehind.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525"
        AllowsTransparency="True" WindowStyle="None">

  <Window.Resources>
    <Storyboard x:Key="storyboard1">
      <DoubleAnimationUsingKeyFrames Storyboard.TargetName="stop1"
                                     Storyboard.TargetProperty="Offset"
                                     Duration="0:0:3">
        <LinearDoubleKeyFrame Value="1" KeyTime="100%"/>
      </DoubleAnimationUsingKeyFrames>
      <DoubleAnimationUsingKeyFrames Storyboard.TargetName="stop2"
                                     Storyboard.TargetProperty="Offset"
                                     Duration="0:0:3">
        <LinearDoubleKeyFrame Value="1" KeyTime="100%"/>
      </DoubleAnimationUsingKeyFrames>
    </Storyboard>
  </Window.Resources>

  <Window.Background>
    <LinearGradientBrush EndPoint="1,1" StartPoint="0,0">
      <GradientStop x:Name="stop1" Color="Black" Offset="0"/>
      <GradientStop x:Name="stop2" Color="White" Offset="1"/>
    </LinearGradientBrush>
  </Window.Background>

  <Window.Triggers>
    <EventTrigger RoutedEvent="Loaded">
      <BeginStoryboard Storyboard="{StaticResource storyboard1}"/>
    </EventTrigger>
  </Window.Triggers>
</Window>