In an XAML document, I have a gradient brush as a resource and a bunch of shapes that use this resource. I would like to animate the brush using a storyboard, but I don't know how to set the brush in resources as the target of storyboard. Simply using its name does not work, {StaticResource name} does not work either. Is it even possible?
I would prefer an XAML only solution, but if that does not work out, I'll use code-behind. If it lets me leave Storyboard.Target and Storyboard.TargetProperty unassigned.
EDIT: I would like to animate a gradient stop of the brush. The thing is that I can animate it easily when it's not a resource, but is applied directly on an object. I can do that by clicking in Expression Blend. I just don't know how to animate it when its a resource (i.e. what to put instead of the ?? in the code below (the storyboard was created for a rectangle))
code:
<UserControl.Resources>
<LinearGradientBrush x:Key="Outline" EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="#7F7CE3FF" Offset="0"/>
<GradientStop Color="#7F047695" Offset="1"/>
<GradientStop Color="#FFFFFFFF" Offset="0.942"/>
</LinearGradientBrush>
<Storyboard x:Key="Glitter">
<DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="??" Storyboard.TargetProperty="(Shape.Fill).(GradientBrush.GradientStops)[0].(GradientStop.Offset)">
<SplineDoubleKeyFrame KeyTime="00:00:00" Value="0"/>
<SplineDoubleKeyFrame KeyTime="00:00:02.6000000" Value="0.529"/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
...
It works when you animate the Background/Fill Property directly, using the name of the object (e.g. Rectangle) you want to animate as Storyboard.TargetName:
<Window x:Class="WpfApplication1.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300">
<Grid>
<Grid.Resources>
<LinearGradientBrush x:Key="Outline" EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="#7F7CE3FF" Offset="0"/>
<GradientStop Color="#7F047695" Offset="1"/>
<GradientStop Color="#FFFFFFFF" Offset="0.942"/>
</LinearGradientBrush>
</Grid.Resources>
<Border Name="border"
Background="{StaticResource Outline}"
Width="200" Height="200" />
</Grid>
<Window.Triggers>
<EventTrigger RoutedEvent="Window.Loaded">
<BeginStoryboard>
<Storyboard>
<DoubleAnimationUsingKeyFrames BeginTime="00:00:00"
Storyboard.TargetName="border"
Storyboard.TargetProperty="(Border.Background).(GradientBrush.GradientStops)[0].(GradientStop.Offset)">
<SplineDoubleKeyFrame KeyTime="00:00:0" Value="0"/>
<SplineDoubleKeyFrame KeyTime="00:00:1" Value="1"/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Window.Triggers>
Edit
From code behind it seems to be totally possible:
XAML:
<Grid Name="grid">
<Grid.Resources>
<LinearGradientBrush x:Key="Outline" EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="#7F7CE3FF" Offset="0"/>
<GradientStop Color="#7F047695" Offset="1"/>
<GradientStop Color="#FFFFFFFF" Offset="0.942"/>
</LinearGradientBrush>
</Grid.Resources>
<Border Background="{StaticResource Outline}"
Width="100" Height="100" HorizontalAlignment="Left" />
<Border Background="{StaticResource Outline}"
Width="100" Height="100" HorizontalAlignment="Right" />
</Grid>
C# code behind:
LinearGradientBrush b = grid.Resources["Outline"] as LinearGradientBrush;
b.GradientStops[0].BeginAnimation(GradientStop.OffsetProperty, new DoubleAnimation(0, 1, new Duration(TimeSpan.FromSeconds(1))));
You cannot animate Properties of the type Brush, you can only animate types that have an appropiate Animation Class, for example DoubleAnimation, PointAnimation or ColorAnimation (note that the last one animates Properties of the type Color, not Brush).
However, some Brushes have DependencyProperties of the type double, that you could animate, for example the StartPoint and EndPoint Properties of the LinearGradientBrush-Class.
If you can elaborate on what the animation should do exactly, maybe we could find a workaround.
Edit:
To animate the Brush it would have to be declared in the scope of your Animation-Trigger, e.g. in the Data- or ControlTemplate. Animating a Resource via its key will not work.
Not sure exactly what you're trying to animate inside of the brush, but animating Brush resources can be very tricky. I don't have time to type everything out, but here's a little 'tutorial' on how to handle it:
Animating Brushes with ObjectAnimationUsingKeyFrames
Related
If I change some resource used as StaticResource then all the controls which referring to is affected.
And it does not in case the resource is referred as DynamicResource.
But how about some resource referred as StaticResource in a DynamicResource?
<Color x:Key="Color">#000000</Color>
<LinearGradientBrush x:Key="GradientBrush" EndPoint="0,0" StartPoint="0,1" >
<GradientStop Color="{StaticResource Color}" Offset="0" />
<GradientStop Color="{StaticResource Color}" Offset="1" />
</LinearGradientBrush>
<DrawingBrush x:Key="TabItemBrush" >
<DrawingBrush.Drawing>
<GeometryDrawing Brush="{StaticResource GradientBrush}">
<GeometryDrawing.Geometry>
<RectangleGeometry Rect="0 100 100 100"/>
</GeometryDrawing.Geometry>
</GeometryDrawing>
</DrawingBrush.Drawing>
</DrawingBrush>
<Style TargetType="{x:Type TabItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TabItem}">
<Grid x:Name="Root">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="SelectionStates">
<VisualState x:Name="Unselected" >
<Storyboard>
<ColorAnimationUsingKeyFrames Storyboard.TargetName="Border"
Storyboard.TargetProperty="(Panel.Background).(DrawingBrush.Drawing).(GeometryDrawing.Brush).(GradientBrush.GradientStops)[0].(GradientStop.Color)">
<EasingColorKeyFrame KeyTime="0" Value="Red" />
</ColorAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="Selected">
<Storyboard>
<ColorAnimationUsingKeyFrames Storyboard.TargetName="Border"
Storyboard.TargetProperty="(Panel.Background).(DrawingBrush.Drawing).(GeometryDrawing.Brush).(GradientBrush.GradientStops)[0].(GradientStop.Color)">
<EasingColorKeyFrame KeyTime="0" Value="Blue" />
</ColorAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Border x:Name="Border" Background="{DynamicResource TabItemBrush}">
<ContentPresenter x:Name="ContentSite" VerticalAlignment="Center"
HorizontalAlignment="Center" ContentSource="Header"
Margin="12,2,12,2" RecognizesAccessKey="True" />
</Border>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Border in TabItem refers TabItemBrush using DynamicResource.
TabItemBrush refers GradientBrush using StaticResource.
GradientBrush refers Color using StaticResource.
<StackPanel>
<TabControl>
<TabItem Header="AAA" >AAA</TabItem>
<TabItem Header="BBB" >BBB</TabItem>
<TabItem Header="CCC" >BBB</TabItem>
</TabControl>
<Border Background="{DynamicResource GradientBrush}" Height="1000" Width="1000"/>
</StackPanel>
I thought that all of TabItem change to same color when I change a selected item in TabControl because all of resources is referred as StaticResource except TabItemBrush.
But only the color of selected item is blue and the others red.
Moreover if i change all the StaticResource to DynamicResource, it works incorrectly ( all red or all blue).
Why StaticResource works as if it is not shared and DynamicResource as shared?
I found your question very difficult to understand. However, I think you are confused because you are not understanding the difference between StaticResource and DynamicResource.
As the names suggest, StaticResource references will/can never change (they are static).
Resources referenced using StaticResource are resolved only once at compile-time.
As the names suggest, DynamicResource references can change (they are dynamic).
Resources referenced using DynamicResource are resolved at runtime.
Since StaticResource references are resolved at compile-time, the XAML parser can avoid the overhead of creating an intermediate lookup expression (which is then executed at runtime).
This is why you should always avoid DynamicResource with the goal to improve the application's performance.
StaticResource does not allow forward references, whereas DynamicResource does - but the forward reference aspect doesn't matter in your context.
"If I change some resource used as StaticResource then all the
controls which referring to is affected."
This is definitely not correct. You can't change resources referenced as StaticResource and then observe that those changes update the referencing objects - this can't have never happened (see explanation of the fundamental characteristics above).
You probably meant something different here.
"I thought that all of TabItem change to same color when I change a selected >item in TabControl because all of resources is referred as StaticResource >except TabItemBrush.
But only the color of selected item is blue and the others red."
This is the correct behavior. You have defined the VisualState objects for "Selected" and "Unselected" with the VisualStateManager. According to these states clicking a TabItem will modify the Background of the selected item to blue and all other unselected items to red.
"Moreover if i change all the StaticResource to DynamicResource, it works >incorrectly ( all red or all blue)."
Here comes in the special behavior of the XAML engine in context of animations: it will freeze the Storyboard.
This means, all participating child instances like AnimationTimeline (e.g., ColorAnimation), or Animatable types in general, are frozen and therefore not changeable (they all inherit Freezable).
As a consequence, all referenced resources like Brush must be static and known at compile-time: you can't reference resources using DynamicResource if the referencing instance needs to be frozen.
Resources used in a Storyboard must be referenced as StaticResource.
Now, given the case that an element, for example a Border, uses DynamicResource to reference a resource that itself contains references to other resources:
If the element uses DynamicResource to reference a pure static resource (a resource that doesn't itself references other resources using DynamicReference), then the XAML engine will optimize the reference by treating it as a StaticResource, to avoid the overhead, and store it in the internal lookup table for the static resources (remember, StaticResource is much cheaper in terms of performance costs than looking up a DynamicResource). This resource can be targeted by a Storyboard animation as it is now static:
<ResourceDictionary>
<Color x:Key="Color">#000000</Color>
<!-- Resource is treated as a static resource -->
<LinearGradientBrush x:Key="GradientBrush" EndPoint="0,0" StartPoint="0,1" >
<GradientStop Color="{StaticResource Color}" Offset="0" />
<GradientStop Color="{StaticResource Color}" Offset="1" />
</LinearGradientBrush>
</ResourceDictionary>
<!-- The lookup behavior is optimized to StaticResource.
A Storyboard therefore will be able to animate the Background resource.
-->
<Border Background="{DynamicResource GradientBrush}" />
If the resource that an element references is itself referencing at least one resource using DynamicResource, then the reference will remain dynamic and will prevent e.g., the Storyboard from being frozen and the animation won't work:
<ResourceDictionary>
<Color x:Key="Color">#000000</Color>
<!-- Resource is treated as a dynamic resource,
because it contains at least one DynamicResource reference.
-->
<LinearGradientBrush x:Key="GradientBrush" EndPoint="0,0" StartPoint="0,1" >
<GradientStop Color="{DynamicResource Color}" Offset="0" />
<GradientStop Color="{StaticResource Color}" Offset="1" />
</LinearGradientBrush>
</ResourceDictionary>
<!-- The lookup behavior is now DynamicResource and not optimized.
A Storyboard won't be able to animate the properties of the resource as it can't be frozen.
-->
<Border Background="{DynamicResource GradientBrush}" />
Another effect you ecounter is that all items have the same Background when you select a TabItem.
This is related to the way the XAML engine handles resources. Referenceing a resource using StaticResource, results in the resource being shared by default.
You have two solutions to fix this issue:
Declare each participating resource of the static reference as non-shared by setting the x:Shared attribute to false:
<ResourceDictionary>
<Color x:Key="Color"
x:Shared="False">#000000</Color>
<LinearGradientBrush x:Key="GradientBrush"
x:Shared="False"
EndPoint="0,0"
StartPoint="0,1" >
<GradientStop Color="{StaticResource Color}" Offset="0" />
<GradientStop Color="{StaticResource Color}" Offset="1" />
</LinearGradientBrush>
</ResourceDictionary>
<Border Background="{StaticResource GradientBrush}" />
Alternatively, define the animated resource as inline. This way each TabItem border will have its own/non-shared Brush resource:
<ResourceDictionary>
<Color x:Key="Color">#000000</Color>
</ResourceDictionary>
<Border>
<Border.Background>
<LinearGradientBrush EndPoint="0,0" StartPoint="0,1" >
<GradientStop Color="{StaticResource Color}" Offset="0" />
<GradientStop Color="{StaticResource Color}" Offset="1" />
</LinearGradientBrush>
</Border.Background>
</Border>
I wish to animate background color change from yellow to red with a flow from left to right. How to achieve this kind of animation in wpf?
A ColorAnimation could provide the gradual change from yellow to red but because you want it to flow from left to right it might be easier to use a LinearGradient.
Set it up like this
GradientStopOffet, color
0, red
0, red
0, yellow
1, yellow
This would make the area completely yellow.
Then you animate the offset of the third gradientstop from 0 to 1
This would slowly turn the area into a gradient from red to yellow.
Once this animation has finished (or is half way) animate the second gradientstop offset from 0 to 1
This would make the entire area red.
By moving the second and third gradientstops the brush will have a 'smooth' color transition between the four gradient stops: between the first and the second, the second and the third and the third and fourth. At the beginning and at the end the transitions between the gradients stops that are at the same offset are not visible and thereby hide the color transition.
Here is an example. Play around with the starting times and durations to make the animation to your liking.
<Window x:Class="WpfApplication2.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">
<Window.Resources>
<Storyboard x:Key="ToRed" >
<DoubleAnimation Storyboard.TargetName="YellowStop"
Storyboard.TargetProperty="Offset"
To="1"
Duration="0:0:1" />
<DoubleAnimation Storyboard.TargetName="RedStop"
Storyboard.TargetProperty="Offset"
BeginTime="0:0:0.5"
To="1"
Duration="0:0:1" />
</Storyboard>
</Window.Resources>
<Grid>
<StackPanel VerticalAlignment="Top"
Orientation="Horizontal">
<Button Click="ToRedButton_Click">To red</Button>
</StackPanel>
<Rectangle Margin="0,50,0,0">
<Rectangle.Fill>
<LinearGradientBrush StartPoint="0,0"
EndPoint="1,0">
<GradientStop Offset="0"
Color="Red" />
<GradientStop x:Name="RedStop"
Offset="0"
Color="Red" />
<GradientStop x:Name="YellowStop"
Offset="0"
Color="Yellow" />
<GradientStop Offset="1"
Color="Yellow" />
</LinearGradientBrush>
</Rectangle.Fill>
</Rectangle>
</Grid>
</Window>
C# code for the button click:
private void ToRedButton_Click(object sender, RoutedEventArgs e)
{
var toRedAnimation = this.FindResource("ToRed") as Storyboard;
if(toRedAnimation != null)
{
toRedAnimation.Begin();
}
}
If you want a hard transition, animate the offset of the redstop at the same start time as the yellow animation.
Here is another setup, it looks different and animates the color:
<Window x:Class="WpfApplication2.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">
<Window.Resources>
<Storyboard x:Key="ToRed2">
<DoubleAnimation Storyboard.TargetName="MiddleStop"
Storyboard.TargetProperty="Offset"
To="1"
Duration="0:0:1" />
<ColorAnimation Storyboard.TargetName="MiddleStop"
Storyboard.TargetProperty="Color"
BeginTime="0:0:1"
To="Red"
Duration="0:0:1" />
</Storyboard>
</Window.Resources>
<Grid>
<StackPanel VerticalAlignment="Top"
Orientation="Horizontal">
<Button Click="ToRedButton_Click">To red</Button>
</StackPanel>
<Rectangle Margin="0,50,0,0">
<Rectangle.Fill>
<LinearGradientBrush StartPoint="0,0"
EndPoint="1,0">
<GradientStop Offset="0"
Color="Red" />
<GradientStop x:Name="MiddleStop"
Offset="0"
Color="Yellow" />
<GradientStop Offset="1"
Color="Yellow" />
</LinearGradientBrush>
</Rectangle.Fill>
</Rectangle>
</Grid>
</Window>
I can suggest you to use blend. This is the most easy way to edit your xaml.
I'm trying to come up with a creative solution to give this particular effect:
My initial idea: A dynamically sized rectangle with a chroma key shader effect will slide into place over the text. However, I do not want to kill the fidelity of the text edges which tends to happen with shaders.
I also considered using the FormattedText class, though I'm not sure it supports what I'm trying to do.
Any suggestions?
EDIT
To clarify, the text will be essentially a 'TabItem'. I would like the highlighted block to float across all tab items to the selected item. They are currently laid out in a Canvas with logic handling their positioning. A simple animation would not suffice it would seem.
This should give you the effect you want. This uses a gradient brush for the color, but it uses 3 gradient stops to make sure that the color changes immediately from one to the next with no gradient in between.
<Window
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:TestingWPF"
mc:Ignorable="d"
x:Class="TestingWPF.TestWindow"
d:DesignWidth="477" d:DesignHeight="214"
Background="Black">
<TextBlock HorizontalAlignment="Center" VerticalAlignment="Center" FontSize="74" FontWeight="Bold">
<TextBlock.Foreground>
<LinearGradientBrush EndPoint="1,0.5" StartPoint="0,0.5">
<GradientStop Color="White" Offset="0"/>
<GradientStop x:Name="WhiteOffset" Color="White" Offset="1"/>
<GradientStop x:Name="GrayOffset" Color="Gray" Offset="1"/>
</LinearGradientBrush>
</TextBlock.Foreground>
<TextBlock.Triggers>
<EventTrigger RoutedEvent="Loaded">
<BeginStoryboard>
<Storyboard Storyboard.TargetProperty="Offset" Duration="0:0:1" RepeatBehavior="Forever">
<DoubleAnimation Storyboard.TargetName="WhiteOffset" From="0" To="1" />
<DoubleAnimation Storyboard.TargetName="GrayOffset" From="0" To="1" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</TextBlock.Triggers>
Some Text
</TextBlock>
</Window>
I have a button that loads more data into a datagrid when clicked. To indicate progress, I change the text of the button to change from "More" to "Loading..." where the periods are animated. Next, I wanted to add a "glint" effect so that the text catches your eye. If you've used an iPhone/iPod Touch, I'm thinking of the effect on the "Slide To Unlock" text of the lock screen.
To do so, I shift a middle, lighter gradient stop from left to right. Because the animation continuously loops, I used offsets outside of the valid range to create a delay between the times when the light gradient stop is actually visible.
I got this implemented but I can tell that for some reason, the light gradient is not starting at the left edge of the text. It starts at about the 'a' in the "Loading". I accepted that and it's been in place for a while, but I'm now coming back to it just to try to understand why. It seems like maybe it's using the measure of the original text when calculating the animation, but I thought the animations were supposed to apply to each other when in the same storyboard. Here is my code:
<UserControl.Resources>
<local:EmptyBatchNumConverter x:Key="emptyBatchNumConverter" />
<BeginStoryboard x:Key="bsbLoadingMore" x:Name="bsbLoadingMore">
<Storyboard x:Name="sbLoadingMore">
<StringAnimationUsingKeyFrames Storyboard.TargetName="txtBtnMoreText" Storyboard.TargetProperty="Text" Duration="0:0:2" FillBehavior="Stop" RepeatBehavior="Forever">
<DiscreteStringKeyFrame Value="Loading" KeyTime="0:0:0" />
<DiscreteStringKeyFrame Value="Loading." KeyTime="0:0:0.5" />
<DiscreteStringKeyFrame Value="Loading.." KeyTime="0:0:1" />
<DiscreteStringKeyFrame Value="Loading..." KeyTime="0:0:1.5" />
</StringAnimationUsingKeyFrames>
<!--Animate the OffSet of the light gradient stop for a "glint" effect. Using -4.5 to 4.5 to delay the visible effect between repeats (and
control the speed relative to the duration). Using an extra .4 seconds to offset the frequency from the text animation. -->
<DoubleAnimation Storyboard.TargetName="gs2" Storyboard.TargetProperty="Offset" From="-4.5" To="4.5" Duration="0:0:2.4" RepeatBehavior="Forever" />
</Storyboard>
</BeginStoryboard>
</UserControl.Resources>
...
<Button Name="btnMore" Grid.Row="1" Style="{StaticResource OasisGridMoreButton}" Click="btnMore_Click" Visibility="Visible" Height="16">
<Button.Content>
<TextBlock Name="txtBtnMoreText" MinWidth="48" Text="More..." /> <!--MinWidth = width of "Loading..."-->
</Button.Content>
<Button.Foreground>
<LinearGradientBrush StartPoint="0.2,0" EndPoint="1,1">
<GradientStop x:Name="gs1" Color="Black" Offset="0"/>
<GradientStop x:Name="gs2" Color="Cyan" Offset="-4.5"/>
<GradientStop x:Name="gs3" Color="Black" Offset="1" />
</LinearGradientBrush>
</Button.Foreground>
</Button>
Here is the problem:
<LinearGradientBrush StartPoint="0.2,0" EndPoint="1,1">
Change it to:
<LinearGradientBrush StartPoint="0,0" EndPoint="1,1">
Here is the full test app I made (slowed down glint to see it better):
<Window
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" mc:Ignorable="d"
x:Class="WpfApplication4.MainWindow"
x:Name="Window"
Title="MainWindow"
Width="640" Height="480">
<Window.Resources>
<Storyboard x:Key="OnLoaded1"/>
</Window.Resources>
<Window.Triggers>
<EventTrigger RoutedEvent="FrameworkElement.Loaded">
<BeginStoryboard x:Name="bsbLoadingMore">
<Storyboard x:Name="sbLoadingMore">
<StringAnimationUsingKeyFrames Storyboard.TargetName="txtBtnMoreText" Storyboard.TargetProperty="Text" Duration="0:0:2" FillBehavior="Stop" RepeatBehavior="Forever">
<DiscreteStringKeyFrame Value="Loading" KeyTime="0:0:0" />
<DiscreteStringKeyFrame Value="Loading." KeyTime="0:0:0.5" />
<DiscreteStringKeyFrame Value="Loading.." KeyTime="0:0:1" />
<DiscreteStringKeyFrame Value="Loading..." KeyTime="0:0:1.5" />
</StringAnimationUsingKeyFrames>
<!--Animate the OffSet of the light gradient stop for a "glint" effect. Using -4.5 to 4.5 to delay the visible effect between repeats (and
control the speed relative to the duration). Using an extra .4 seconds to offset the frequency from the text animation. -->
<DoubleAnimation Storyboard.TargetName="gs2" Storyboard.TargetProperty="Offset" From="-4.5" To="4.5" Duration="0:0:5.4" RepeatBehavior="Forever" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Window.Triggers>
<Grid x:Name="LayoutRoot">
<Button x:Name="btnMore" Visibility="Visible" Margin="0,213,0,182" d:LayoutOverrides="GridBox">
<Button.Foreground>
<LinearGradientBrush StartPoint="0,0" EndPoint="1,1">
<GradientStop x:Name="gs1" Color="Black" Offset="0"/>
<GradientStop x:Name="gs2" Color="Cyan" Offset="0"/>
<GradientStop x:Name="gs3" Color="Black" Offset="1" />
</LinearGradientBrush>
</Button.Foreground>
<TextBlock x:Name="txtBtnMoreText" MinWidth="48" Text="More..." />
</Button>
</Grid>
For some reason, it is not showing the </Window> at the end...
I have written a control and successfully created a storyboard to cause an animation during triggered events. It changes the fill of an ellipse for a duration of time. Instead of writing a new RadialGradientBrush each time I need to change the fill, I provided two of them in the resources.
EDIT:
I have an Ellipse that is the main component to the control and is what is affected by the animation. It's implementation is simple and looks like this:
<Ellipse Name="myEllipse" Style="{StaticResource DimStyle}" />
When I add it to the storyboard (instead of referencing the brush as a resource), my animation works as intended. When I reference the brush as a resource I get this exception:
"Cannot find resource named 'IlluminatedStyle'. Resource names are case sensitive."
Inside of the storyboard, this is where it is currently referenced:
<UserControl.Resources>
<Storyboard x:Key="Foo">
<ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames.KeyFrames>
<DiscreteObjectKeyFrame KeyTime="0:0:0.01" Value="{StaticResource IlluminatedStyle}" />
<DiscreteObjectKeyFrame KeyTime="0:0:0.85" Value="{StaticResource DimStyle}" />
</ObjectAnimationUsingKeyFrames.KeyFrames>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</UserControl.Resources>
The styles are closely identical and only the GradientStop color properties differ so I'll provide only one style for an example.
The Style Referenced
<UserControl.Resources>
<Style x:Key="IlluminatedStyle" TargetType="Ellipse">
<Setter Property="Fill">
<Setter.Value>
<RadialGradientBrush>
<GradientStop Color="#FF215416" Offset="1"/>
<GradientStop Color="#FE38DA2E" Offset="0"/>
<GradientStop Color="#FE81FF79" Offset="0.688"/>
</RadialGradientBrush>
</Setter.Value>
</Setter>
</Style>
</UserControl.Resources>
So how do I correctly reference a style such as this in my Storyboard?
Note: The Storyboard and Style are both contained within the same UserControl.Resources tag but broken out for this example.
EDIT
I put the Style before the Storyboard in UserControl.Resources and now I get an exception stating:
"This Freezable cannot be frozen.
at System.Windows.Freezable.Freeze()
at System.Windows.Freezable.GetCurrentValueAsFrozen()
at System.Windows.Media.Animation.TimelineCollection.GetCurrentValueAsFrozenCore(Freezable source)
at System.Windows.Freezable.CloneCoreCommon(Freezable sourceFreezable, Boolean useCurrentValue, Boolean cloneFrozenValues)
at System.Windows.Media.Animation.Timeline.GetCurrentValueAsFrozenCore(Freezable sourceFreezable)
at System.Windows.Freezable.GetCurrentValueAsFrozen()
at System.Windows.Media.Animation.Clock..ctor(Timeline timeline)
at System.Windows.Media.Animation.TimelineGroup.AllocateClock()
at System.Windows.Media.Animation.Clock.AllocateClock(Timeline timeline, Boolean hasControllableRoot)"
There are three reasons why a Freezable cannot be frozen:
It has animated or data bound properties.
It has properties that are set by a dynamic resource.
It contains Freezable sub-objects that cannot be frozen.
So, first find out which Freezable is causing trouble and then check the above.
Seeing that I am new to WPF and XAML I made the mistake of making my resources a style and did not realize that I could have simply made the brushes a resource and avoid styles altogether.
I kept the reference to the DiscreteObjectKeyFrames' values as static to the new brush resources. I changed the Ellipse to this:
<Ellipse Name="myEllipse" Fill="{StaticResource DimBrush" />
The style property was removed and I assigned the brush to the fill property directly. In the ObjectAnimationUsingKeyFrames tag I added Fill as the Storyboard.TargetProperty since I was no longer using a style to dress up the fill. The DiscreteObjectKeyFrames now look like this:
<DiscreteObjectKeyFrame KeyTime="0:0:0.01" Value="{StaticResource IlluminatedBrush}" />
<DiscreteObjectKeyFrame KeyTime="0:0:0.85" Value="{StaticResource DimBrush}" />
My resources are much simpler without being wrapped in a style and IMO, more elegant. Also the brushes are defined before the animation in my final solution.
<UserControl.Resources>
<RadialGradientBrush x:Key="DimBrush" >
<GradientStop Color="#FF21471A" Offset="1"/>
<GradientStop Color="#FF33802F" Offset="0"/>
<GradientStop Color="#FF35932F" Offset="0.688"/>
</RadialGradientBrush>
<RadialGradientBrush x:Key="IlluminatedBrush">
<GradientStop Color="#FF215416" Offset="1"/>
<GradientStop Color="#FE38DA2E" Offset="0"/>
<GradientStop Color="#FE81FF79" Offset="0.688"/>
</RadialGradientBrush>
<!-- Storyboard code follows... -->
</UserControl.Resources>
Everything is now working as intended. The best assumption I can make is that styles are not freezable since they were the components that I removed and I no longer receive exceptions regarding a freezable that cannot be frozen.