Раскадровка не меняется при смене стиля в WPF
Я хочу запустить простую анимацию на изображении стрелки (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. Я нашел несколько сообщений в Stackru, согласно которым этот пример должен работать (среди тех, кто утверждал, что это не будет:-)).
Итак... теперь я понятия не имею, как сделать это правильно. Буду благодарен за любую помощь. Спасибо!
Это измененный 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
1 ответ
По стечению обстоятельств я только что нашел ответ на свой вопрос. Имена раскадровок не могут быть решены, потому что они были определены в базовых стилях. Когда я определил их непосредственно в двух интересующих меня стилях, я мог запускать и останавливать их, используя свойство Animate ImageWithAnim, как и ожидалось, без каких-либо исключений.
Итак, при изменении стиля изображения, я должен сделать следующее:
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>
И объяснить, почему я поместил раскадровки в базовые стили в начале. Дело в том, что в моем исходном приложении у меня есть несколько разных изображений стрелок, и я просто не хотел повторять определения раскадровки. Но похоже, что я должен.