Brush to Brush Animation
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