【发布时间】:2016-06-14 09:06:26
【问题描述】:
我想在箭头图片 (png) 上运行一个简单的动画。箭头指向下方或上方,动画应该是沿着箭头指向的方向穿过箭头的波浪。
我使用 Image 控件并为其指定两种样式中的一种。这些样式定义了要使用的图片和故事板中的三个双重动画。动画应该从创建图像的那一刻起,永远无条件地运行。其中一种样式是向上箭头且波浪向上移动 (Trend_Rising),另一种样式是向下箭头且波浪向下 (Trend_Falling)。
以下是图片,样式在一个单独的文件中,从嵌入图片的用户控件引用。
<Image x:Name="TrendImg" Style="{DynamicResource Trend_Falling}" />
这是样式文件的内容:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Style x:Key="Trend_Base" TargetType="Image">
<Setter Property="OpacityMask">
<Setter.Value>
<LinearGradientBrush StartPoint="0,1" EndPoint="0,0">
<GradientStop Color="Black" />
<GradientStop Color="Transparent" />
<GradientStop Color="Black" />
</LinearGradientBrush>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="Trend_Rising_Base" TargetType="Image" BasedOn="{StaticResource Trend_Base}">
<Style.Triggers>
<Trigger Property="IsVisible" Value="True">
<Trigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="OpacityMask.GradientStops[0].Offset" From="-0.1" To="0.9" Duration="0:0:2" RepeatBehavior="Forever" />
<DoubleAnimation Storyboard.TargetProperty="OpacityMask.GradientStops[1].Offset" From="0.0" To="1.0" Duration="0:0:2" RepeatBehavior="Forever" />
<DoubleAnimation Storyboard.TargetProperty="OpacityMask.GradientStops[2].Offset" From="0.1" To="1.1" Duration="0:0:2" RepeatBehavior="Forever" />
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
</Trigger>
</Style.Triggers>
</Style>
<Style x:Key="Trend_Falling_Base" TargetType="Image" BasedOn="{StaticResource Trend_Base}">
<Style.Triggers>
<Trigger Property="IsVisible" Value="True">
<Trigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="OpacityMask.GradientStops[0].Offset" From="0.9" To="-0.1" Duration="0:0:2" RepeatBehavior="Forever" />
<DoubleAnimation Storyboard.TargetProperty="OpacityMask.GradientStops[1].Offset" From="1.0" To="0.0" Duration="0:0:2" RepeatBehavior="Forever" />
<DoubleAnimation Storyboard.TargetProperty="OpacityMask.GradientStops[2].Offset" From="1.1" To="0.1" Duration="0:0:2" RepeatBehavior="Forever" />
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
</Trigger>
</Style.Triggers>
</Style>
<Style x:Key="Trend_Rising" TargetType="Image" BasedOn="{StaticResource Trend_Rising_Base}">
<Setter Property="Height" Value="16" />
<Setter Property="Source" Value="trend_rising.png" />
</Style>
<Style x:Key="Trend_Falling" TargetType="Image" BasedOn="{StaticResource Trend_Falling_Base}">
<Setter Property="Height" Value="16" />
<Setter Property="Source" Value="trend_falling.png" />
</Style>
</ResourceDictionary>
问题是当我以编程方式更改样式时,动画不会改变。例如,如果我启动应用程序(图像分配了 Trend_Falling 样式),将显示向下箭头,并且波浪动画应该会向下移动。但是当我在运行时将样式更改为 Trend_Rising 时,箭头图片会发生应有的变化,但动画保持不变。
TrendImg.SetResourceReference(Control.StyleProperty, "Trend_Rising")
我做错了什么?我将不胜感激任何帮助。谢谢!
-- 编辑--
我创建了一个 ImageWithAnim 类,它是 Image 的后代,并向它添加了一个布尔 Animate 依赖属性。然后我将触发器附加到该属性而不是 IsVisible。 True 启动情节提要,而 false 应该停止它,但它不会...当我将 Animate 设置为 false 时,会抛出一个异常,说明名称 RisingStoryboard 可以不能在命名空间 System.Windows.Style 中解析。我在 StackOverflow 上找到了几篇文章,根据这些文章,这个例子应该可以工作(其中那些声称它不会:-))。
所以...现在我不知道如何正确地做到这一点。我将不胜感激任何帮助。谢谢!
这是更改后的 xaml:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:Test="clr-namespace:StyleChangeTest">
<Style x:Key="Trend_Base" TargetType="Test:ImageWithAnim">
<Setter Property="OpacityMask">
<Setter.Value>
<LinearGradientBrush StartPoint="0,1" EndPoint="0,0">
<GradientStop Color="Black" />
<GradientStop Color="Transparent" />
<GradientStop Color="Black" />
</LinearGradientBrush>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="Trend_Rising_Base" TargetType="Test:ImageWithAnim" BasedOn="{StaticResource Trend_Base}">
<Style.Triggers>
<Trigger Property="Animate" Value="True">
<Trigger.EnterActions>
<BeginStoryboard x:Name="RisingStoryboard">
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="OpacityMask.GradientStops[0].Offset" From="-0.1" To="0.9" Duration="0:0:2" RepeatBehavior="Forever" />
<DoubleAnimation Storyboard.TargetProperty="OpacityMask.GradientStops[1].Offset" From="0.0" To="1.0" Duration="0:0:2" RepeatBehavior="Forever" />
<DoubleAnimation Storyboard.TargetProperty="OpacityMask.GradientStops[2].Offset" From="0.1" To="1.1" Duration="0:0:2" RepeatBehavior="Forever" />
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
</Trigger>
<Trigger Property="Animate" Value="False">
<Trigger.EnterActions>
<StopStoryboard BeginStoryboardName="RisingStoryboard" />
</Trigger.EnterActions>
</Trigger>
</Style.Triggers>
</Style>
<Style x:Key="Trend_Falling_Base" TargetType="Test:ImageWithAnim" BasedOn="{StaticResource Trend_Base}">
<Style.Triggers>
<Trigger Property="Animate" Value="True">
<Trigger.EnterActions>
<BeginStoryboard x:Name="FallingStoryboard">
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="OpacityMask.GradientStops[0].Offset" From="0.9" To="-0.1" Duration="0:0:2" RepeatBehavior="Forever" />
<DoubleAnimation Storyboard.TargetProperty="OpacityMask.GradientStops[1].Offset" From="1.0" To="0.0" Duration="0:0:2" RepeatBehavior="Forever" />
<DoubleAnimation Storyboard.TargetProperty="OpacityMask.GradientStops[2].Offset" From="1.1" To="0.1" Duration="0:0:2" RepeatBehavior="Forever" />
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
</Trigger>
<Trigger Property="Animate" Value="False">
<Trigger.EnterActions>
<StopStoryboard BeginStoryboardName="FallingStoryboard" />
</Trigger.EnterActions>
</Trigger>
</Style.Triggers>
</Style>
<Style x:Key="Trend_Rising" TargetType="Test:ImageWithAnim" BasedOn="{StaticResource Trend_Rising_Base}">
<Setter Property="Height" Value="16" />
<Setter Property="Source" Value="trend_rising.png" />
</Style>
<Style x:Key="Trend_Falling" TargetType="Test:ImageWithAnim" BasedOn="{StaticResource Trend_Falling_Base}">
<Setter Property="Height" Value="16" />
<Setter Property="Source" Value="trend_falling.png" />
</Style>
</ResourceDictionary>
这是 ImageWithAnim 类:
Public Class ImageWithAnim
Inherits Image
Private Shared _animate As DependencyProperty = DependencyProperty.Register("Animate",
GetType(Boolean),
GetType(ImageWithAnim),
New PropertyMetadata(defaultValue:=False))
Public Shared ReadOnly Property AnimateProperty As DependencyProperty
Get
Return _animate
End Get
End Property
Public Property Animate() As Boolean
Get
Return CBool(GetValue(_animate))
End Get
Set(value As Boolean)
SetValue(_animate, value)
End Set
End Property
End Class
【问题讨论】:
-
您的目标只是一个 Image TargetType。所以它并不关心图像本身是否发生了变化,只要有一个图像就可以了。如果您要走这条路,那么当您更改图像源时,您需要手动停止/开始在单独的动画之间进行交换。
-
谢谢,@ChrisW。但是你会用什么方式停止动画并重新启动它?我在某种程度上了解 WPF,但我不是专家。可以使用样式以简单的方式完成,还是应该以编程方式完成?我想到的第一个想法是创建一个带有属性的 Image 类后代,该属性决定动画应该开始还是停止。然后使用该类而不是 Image 并将触发器附加到该属性而不是 IsVisible。这是一个好主意还是可以更简单地完成?
-
我用布尔 Animate 依赖属性创建了 Image 的后代。我在样式中使用它而不是 IsVisible,并且我设法通过将 Animate 设置为 True 来启动动画。但是,通过将 Animate 设置为 False 来停止它不起作用。因此,我添加了附加到 Animate=False 的第二个触发器,其中的 StopStoryboard 元素将 BeginStoryBoardName 设置为为 Animate=True 定义的 BeginStoryboard 的 x:Name。现在每次我将 Animate 设置为 False 时,我都会收到一个异常,指出无法在 System.Windows.Style 命名空间中解析指定的 BeginStoryboard 名称。我被困住了。
-
好的,当我找到一些空闲时间时,我会尝试回到这个。这周的事情比平时多得多。
-
没关系,我不想打扰你太多。感谢您的帮助,谢谢!
标签: .net wpf xaml wpf-style wpf-animation