【问题标题】:Separate animations work, Storyboard doesn't. Why?单独的动画有效,Storyboard 无效。为什么?
【发布时间】:2016-11-27 00:26:12
【问题描述】:

编辑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 不起作用。

【问题讨论】:

  • 在代码隐藏中正确组装必要的配置可能非常棘手。它在 XAML 中通常更容易,也更易于维护,这是首选方法。如果您确实需要有关代码隐藏版本的帮助,请提供一个好的 minimal reproducible example 可靠地重现特定问题,即您认为 应该 工作但没有工作的代码。我不认为“丑陋的黑客”(正如你所说的那样)有用,但如果你认为它属于问题,请确保它是一个完全独立的代码示例。
  • 还要发布 DAUKF 里面的内容,因为这将被添加到 SB。
  • @PeterDuniho 请参阅编辑以获取最小、完整和可验证的示例。
  • 我已经编辑了您的帖子,以便您发布的代码可以编译。通常,在问题中编辑代码是不受欢迎的,因为它可能会将问题更改为作者想要提出的问题。但在这种情况下,我假设您实际处理的任何代码都确实编译,并且无论出于何种原因,您都没有费心创建一个实际的minimal reproducible example 以包含在您的问题中可以只是复制/粘贴。为了将来参考,请确保您问题中的代码始终是从实际工作示例中直接复制/粘贴的。

标签: c# wpf animation storyboard gradient


【解决方案1】:

出于某种原因,Storyboard.SetTarget 仅适用于 FrameworkElements 或 FrameworkContentElements。为了做你想做的事,你可以像在“hack”中那样自己启动单个动画(一种完全合理的动画制作方式,IMO)。

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

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

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

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

【讨论】:

  • 这只是概念证明。我将需要能够在一角钱上开始(和停止)动画。能够一次完成这一切会很有帮助。此外,我还需要更多的控制,因为可以随时向(或从)集合添加(或删除)渐变停止,除了如何更改它们的停止位置......这只是一个大一团糟,但我很确定你的答案就是我正在寻找的答案。
【解决方案2】:

正如the other answer 中所述,这是 WPF 的一个未记录的(据我所知)限制。称其为错误。有关更多详细信息,请参阅以前的帖子,例如 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>

【讨论】:

  • 不幸的是,所讨论的渐变是由控件定义到模型中的,而不是在 XAML 中,也不可能改变它。出于这个原因,我将努力利用下面的建议来命名后面代码中的渐变,看看是否能完成这项工作。
猜你喜欢
  • 2010-10-15
  • 1970-01-01
  • 2014-06-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-01-26
  • 2022-01-12
  • 2020-05-14
相关资源
最近更新 更多