I managed to find out how to make a WPF animation - transition between two colors.
It's called ColorAnimation and works well.
ColorAnimation animation = new ColorAnimation
{
From = Colors.DarkGreen,
To = Colors.Transparent,
Duration = new Duration(TimeSpan.FromSeconds(1.5)),
AutoReverse = false
};
animation.Completed += new EventHandler(animation_Completed);
SolidColorBrush brush = new SolidColorBrush(Colors.Transparent);
animation.AccelerationRatio = 0.5;
Background = brush;
brush.BeginAnimation(SolidColorBrush.ColorProperty, animation);
I am using this to animate background of my usercontrol. My controls background is SolidColorBrush
. Recently I changed to LinearGradientBrush
. Now I can use my animation no more.
I need animation from brush to brush, not color to color. And best option is Abstract brush type, which includes SolidColor, LinearGradient etc, so I can animate for example from SolidColorBrush
to LinearGradientBrush
. Is that even possible? Thank you.
Another possible way is, to create a custom animtion class that animate brushes.
I found a simple way to do that by creating a class, derivated from AnimationTimeline
. We can override some members in the custom class, among other things the AnimationTimeline.GetCurrentValue
method. It returns a value depend on the animation progress and the start- and end value.
The simplest way is to create a VisualBrush
and crossfade the start- with the end value with the Opacity
property on a child control. The result is a class like the following:
public class BrushAnimation : AnimationTimeline
{
public override Type TargetPropertyType
{
get
{
return typeof(Brush);
}
}
public override object GetCurrentValue(object defaultOriginValue,
object defaultDestinationValue,
AnimationClock animationClock)
{
return GetCurrentValue(defaultOriginValue as Brush,
defaultDestinationValue as Brush,
animationClock);
}
public object GetCurrentValue(Brush defaultOriginValue,
Brush defaultDestinationValue,
AnimationClock animationClock)
{
if (!animationClock.CurrentProgress.HasValue)
return Brushes.Transparent;
//use the standard values if From and To are not set
//(it is the value of the given property)
defaultOriginValue = this.From ?? defaultOriginValue;
defaultDestinationValue = this.To ?? defaultDestinationValue;
if (animationClock.CurrentProgress.Value == 0)
return defaultOriginValue;
if (animationClock.CurrentProgress.Value == 1)
return defaultDestinationValue;
return new VisualBrush(new Border()
{
Width = 1,
Height = 1,
Background = defaultOriginValue,
Child = new Border()
{
Background = defaultDestinationValue,
Opacity = animationClock.CurrentProgress.Value,
}
});
}
protected override Freezable CreateInstanceCore()
{
return new BrushAnimation();
}
//we must define From and To, AnimationTimeline does not have this properties
public Brush From
{
get { return (Brush)GetValue(FromProperty); }
set { SetValue(FromProperty, value); }
}
public Brush To
{
get { return (Brush)GetValue(ToProperty); }
set { SetValue(ToProperty, value); }
}
public static readonly DependencyProperty FromProperty =
DependencyProperty.Register("From", typeof(Brush), typeof(BrushAnimation));
public static readonly DependencyProperty ToProperty =
DependencyProperty.Register("To", typeof(Brush), typeof(BrushAnimation));
}
You can use it as always in XAML:
<EventTrigger RoutedEvent="Loaded">
<BeginStoryboard>
<Storyboard >
<local:BrushAnimation Storyboard.TargetName="border"
Storyboard.TargetProperty="Background"
Duration="0:0:5" From="Red"
RepeatBehavior="Forever" AutoReverse="True" >
<local:BrushAnimation.To>
<LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="#FF00FF2E" Offset="0.005"/>
<GradientStop Color="#FFC5FF00" Offset="1"/>
<GradientStop Color="Blue" Offset="0.43"/>
</LinearGradientBrush>
</local:BrushAnimation.To>
</local:BrushAnimation>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
or in code behind:
var animation = new BrushAnimation
{
From = Brushes.Red,
To = new LinearGradientBrush (Colors.Green, Colors.Yellow, 45),
Duration = new Duration(TimeSpan.FromSeconds(5)),
};
animation.Completed += new EventHandler(animation_Completed);
Storyboard.SetTarget(animation, border);
Storyboard.SetTargetProperty(animation, new PropertyPath("Background"));
var sb = new Storyboard();
sb.Children.Add(animation);
sb.Begin();
It is also possible to extend the BrushAnimation
with constructor overloads etc., so it looks like a .NET given animation type.
You just need to use the color animation on the gradient stops of the gradient brush. Here is an example that animates a rectangle gradient using a storyboard.
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="GradientBrushAnimation.MainWindow"
x:Name="Window"
Title="MainWindow"
Width="640" Height="480">
<Window.Resources>
<Storyboard x:Key="Storyboard1">
<ColorAnimationUsingKeyFrames Storyboard.TargetProperty="(Shape.Fill).(GradientBrush.GradientStops)[0].(GradientStop.Color)" Storyboard.TargetName="rectangle">
<EasingColorKeyFrame KeyTime="0:0:2" Value="Red"/>
</ColorAnimationUsingKeyFrames>
<ColorAnimationUsingKeyFrames Storyboard.TargetProperty="(Shape.Fill).(GradientBrush.GradientStops)[1].(GradientStop.Color)" Storyboard.TargetName="rectangle">
<EasingColorKeyFrame KeyTime="0:0:2" Value="#FF71FF00"/>
</ColorAnimationUsingKeyFrames>
</Storyboard>
</Window.Resources>
<Window.Triggers>
<EventTrigger RoutedEvent="FrameworkElement.Loaded">
<BeginStoryboard Storyboard="{StaticResource Storyboard1}"/>
</EventTrigger>
</Window.Triggers>
<Grid x:Name="LayoutRoot">
<Rectangle x:Name="rectangle" Margin="78,102,292,144" Stroke="Black">
<Rectangle.Fill>
<LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="Black" Offset="0"/>
<GradientStop Color="White" Offset="1"/>
</LinearGradientBrush>
</Rectangle.Fill>
</Rectangle>
</Grid>
</Window>
You can animate the color of the brush if you have a template style in which you give the fill brush a name, like so:
<Rectangle Width="100" Height="100">
<Rectangle.Fill>
<SolidColorBrush x:Name="MyAnimatedBrush" Color="Orange" />
</Rectangle.Fill>
<Rectangle.Triggers>
<!-- Animates the brush's color to gray
When the mouse enters the rectangle. -->
<EventTrigger RoutedEvent="Rectangle.MouseEnter">
<BeginStoryboard>
<Storyboard>
<ColorAnimation
Storyboard.TargetName="MyAnimatedBrush"
Storyboard.TargetProperty="Color"
To="Gray" Duration="0:0:1" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Rectangle.Triggers>
</Rectangle>
As taken from MSDN