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
不起作用。
答案 0 :(得分:1)
出于某种原因,Storyboard.SetTarget
仅适用于FrameworkElement
或FrameworkContentElement
。要做你想做的事,你可以像在“黑客”中那样自己动手制作动画(完全合理的动画方式,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 work和Why 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>