【问题标题】:Storyboard does not change when I change style in WPF当我在 WPF 中更改样式时,情节提要不会改变
【发布时间】: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


【解决方案1】:

巧合的是,我刚刚找到了自己问题的答案。故事板的名称无法解析,因为它们是在基本样式中定义的。当我直接在我感兴趣的两种样式中定义它们时,我可以按预期使用 ImageWithAnimAnimate 属性来启动和停止它们,没有任何例外。

所以,在改变图片的样式时,我必须做到以下几点:

TrendImg.Animate = False
TrendImg.SetResourceReference(ImageWithAnim.StyleProperty, "Trend_Falling")
TrendImg.Animate = True

现在动画改好了,波浪动画朝着箭头所指的方向移动,这就是我想要实现的。

这是改变后的风格:

<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" TargetType="Test:ImageWithAnim" BasedOn="{StaticResource Trend_Base}">
        <Setter Property="Height" Value="16" />
        <Setter Property="Source" Value="trend_rising.png" />

        <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.ExitActions>
                    <StopStoryboard BeginStoryboardName="RisingStoryboard" />
                </Trigger.ExitActions>
            </Trigger>
        </Style.Triggers>
    </Style>

    <Style x:Key="Trend_Falling" TargetType="Test:ImageWithAnim" BasedOn="{StaticResource Trend_Base}">
        <Setter Property="Height" Value="16" />
        <Setter Property="Source" Value="trend_falling.png" />

        <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.ExitActions>
                    <StopStoryboard BeginStoryboardName="FallingStoryboard" />
                </Trigger.ExitActions>
            </Trigger>
        </Style.Triggers>
    </Style>

</ResourceDictionary>

并解释为什么我一开始就把故事板放在基本样式中。问题是在我的原始应用程序中,我有几个不同的箭头图片,只是不想重复故事板定义。但看来我必须这样做。

【讨论】:

    猜你喜欢
    • 2010-11-30
    • 1970-01-01
    • 2015-05-05
    • 1970-01-01
    • 2011-01-01
    • 2019-10-09
    • 1970-01-01
    • 2016-12-29
    • 2011-01-23
    相关资源
    最近更新 更多