Trouble animating RadialGradientBrush in WPF
I'm trying to animate a RadialGradientBrush
in my application. I get the super helpful exception:
Additional information: 'System.Windows.Style' value cannot be assigned to property 'Style' of object 'System.Windows.Controls.Border'. '[Unknown]' property does not point to a DependencyObject in path '(0).(1).[0].(2)'. Error at object 'System.Windows.Style' in markup file 'Eng.Modules.Core;component/system/grid/systemgridview.xaml' Line 252 Position 51.
I know it's something wrong with the indirect property targeting or partial path qualification in my DoubleAnimation
's Storyboard.TargetProperty
attribute. Any ideas?
<Border>
<Border.Resources>
<RadialGradientBrush x:Key="SomeBrush">
<RadialGradientBrush.GradientStops>
<GradientStop Colo开发者_开发知识库r="White" Offset="0" />
<GradientStop Color="Gold" Offset="1" />
</RadialGradientBrush.GradientStops>
</RadialGradientBrush>
</Border.Resources>
<Border.Style>
<Style TargetType="{x:Type Border}">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=IsEnabled, RelativeSource={RelativeSource Self}}" Value="True">
<Setter Property="Background" Value="{StaticResource SomeBrush}" />
<DataTrigger.EnterActions>
<BeginStoryboard x:Name="SomeStoryBoard">
<Storyboard>
<!-- RIGHT HERE -->
<DoubleAnimation
Storyboard.TargetProperty="(Border.Background).(GradientBrush.GradientStops)[0].(GradientStop.Offset)"
From="0" To="1" Duration="0:0:1"
RepeatBehavior="Forever"
AutoReverse="True" />
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<RemoveStoryboard BeginStoryboardName="SomeStoryBoard" />
</DataTrigger.ExitActions>
</DataTrigger>
</Style.Triggers>
</Style>
</Border.Style>
</Border>
The first problem is that you are setting the background to a gradient brush in your DataTrigger
. Because this gets applied later, the animation won't be able to find the brush (hence the cryptic error about not finding a dependency property). So the first thing I did was set the border's background to the brush manually, rather than in the trigger.
The second problem was how you were setting up the target property. You don't need to use the parenthesis syntax- it works just fine as follows: Background.GradientStops[0].Offset
.
With these changes the border animates perfectly; here is the final mark-up:
<Border>
<Border.Background>
<RadialGradientBrush>
<RadialGradientBrush.GradientStops>
<GradientStop Color="White" Offset="0" />
<GradientStop Color="Gold" Offset="1" />
</RadialGradientBrush.GradientStops>
</RadialGradientBrush>
</Border.Background>
<Border.Style>
<Style TargetType="{x:Type Border}">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=IsEnabled, RelativeSource={RelativeSource Self}}" Value="True">
<DataTrigger.EnterActions>
<BeginStoryboard x:Name="SomeStoryBoard">
<Storyboard>
<!-- RIGHT HERE -->
<DoubleAnimation
Storyboard.TargetProperty="Background.GradientStops[0].Offset"
From="0" To="1" Duration="0:0:1"
RepeatBehavior="Forever"
AutoReverse="True" />
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<RemoveStoryboard BeginStoryboardName="SomeStoryBoard" />
</DataTrigger.ExitActions>
</DataTrigger>
</Style.Triggers>
</Style>
</Border.Style>
</Border>
My Answer as per the comments in the code sample.
I've removed the MultiDataTrigger from my first post, as I can't get this working myself with bound collections. I may have to ask this to see if anyone has an answer to the issue.
<Grid>
<Grid.Resources>
<!-- Don't use a ControlTemplate. This destroys your control structure,
which doesn't make sense if all you want to do is animate the background. -->
<Style x:Key="MyAnimatedFeatureStyle" TargetType="Button">
<Style.Resources>
<RadialGradientBrush x:Key="GoldRadialGradientBrush">
<RadialGradientBrush.GradientStops>
<GradientStop Color="White" Offset="0" />
<GradientStop Color="Gold" Offset="1" />
</RadialGradientBrush.GradientStops>
</RadialGradientBrush>
<Storyboard RepeatBehavior="Forever" AutoReverse="False" x:Key="SomeStoryBoard">
<DoubleAnimation Storyboard.TargetProperty="Background.GradientStops[0].Offset" From="0" To="1" Duration="0:0:1" RepeatBehavior="Forever" AutoReverse="True" />
</Storyboard>
</Style.Resources>
<Style.Triggers>
<!-- Your primary binding condition. Whatever this may be. -->
<DataTrigger Binding="{Binding Path=IsSOMTHINGSET}" Value="true" >
<!-- The Background is here in the setter, because we only want the background colored and animated on the binding condition. -->
<Setter Property="Background" Value="{StaticResource GoldRadialGradientBrush}" />
<DataTrigger.EnterActions>
<BeginStoryboard x:Name="BorderStoryBoard" Storyboard="{StaticResource SomeStoryBoard}" />
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<RemoveStoryboard BeginStoryboardName="BorderStoryBoard" />
</DataTrigger.ExitActions>
</DataTrigger>
</Style.Triggers>
</Style>
</Grid.Resources>
<Button Style="{StaticResource MyAnimatedFeatureStyle}" />
</Grid>
I actually developed a solution to the problem just as Charlie posted his. It looks like we came up with close to the same thing. Just posting mine for reference:
<Button>
<Button.Style>
<Style TargetType="{x:Type Button}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<ControlTemplate.Resources>
<RadialGradientBrush x:Key="GoldRadialGradientBrush">
<RadialGradientBrush.GradientStops>
<GradientStop Color="White" Offset="0" />
<GradientStop Color="Gold" Offset="1" />
</RadialGradientBrush.GradientStops>
</RadialGradientBrush>
<Storyboard x:Key="SomeStoryBoard">
<DoubleAnimation
Storyboard.TargetProperty="(Border.Background).(GradientBrush.GradientStops)[0].Offset"
From="0" To="1" Duration="0:0:1"
RepeatBehavior="Forever"
AutoReverse="True" />
</Storyboard>
</ControlTemplate.Resources>
<Grid>
<Border Background="{StaticResource GoldRadialGradientBrush}">
<Border.Style>
<Style TargetType="{x:Type Border}">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=IsEnabled, RelativeSource={RelativeSource Self}}" Value="True">
<DataTrigger.EnterActions>
<BeginStoryboard x:Name="BorderStoryBoard" Storyboard="{StaticResource SomeStoryBoard}" />
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<RemoveStoryboard BeginStoryboardName="BorderStoryBoard" />
</DataTrigger.ExitActions>
</DataTrigger>
</Style.Triggers>
</Style>
</Border.Style>
</Border>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Button.Style>
</Button>
精彩评论