I need help in taking right decision. I need to animate a background color of my user control when some event happens. When it is, I want to change the background just for 1 second and then turn it back. Which way should I go? Use color animation or timer or may by some other way.
Solved. Thanks to all! This works good for me:
ColorAnimation animation;
animation = new ColorAnimation();
animation.From = Colors.Orange;
animation.To = Colors.Gray;
animation.Duration = new Duration(TimeSpan.FromSeconds(1));
this.elGrid.Background.BeginAnimation(SolidColorBrush.ColorProperty, animation);
I would use an EventTrigger
with a ColorAnimation
.
In this example a Button Brackground
goes green on a MouseLeave
event. This code is hopefully similar to what you may need.
<Button Content="Button" Height="75" HorizontalAlignment="Left" Margin="27,12,0,0" Name="btnImgBrush" VerticalAlignment="Top" Width="160" Background="LightGray">
<Button.Triggers>
<EventTrigger RoutedEvent="Button.MouseLeave">
<BeginStoryboard>
<Storyboard>
<ColorAnimation To="Green"
Storyboard.TargetProperty="(Button.Background).(SolidColorBrush.Color)"
FillBehavior="Stop"
Duration="0:0:1"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Button.Triggers>
</Button>
Watch out, you could received a System.InvalidOperationException if your background is a frozen instance.
Cannot animate the 'Color' property on 'System.Windows.Media.SolidColorBrush' because the object is sealed or frozen.
To correct this message assign the background of your control to a non-frozen instance.
// Do not use a frozen instance
this.elGrid.Background = new SolidColorBrush(Colors.Orange);
this.elGrid.Background.BeginAnimation(SolidColorBrush.ColorProperty, animation);
Freezable Objects Overview on MSDN
ColorAnimation colorChangeAnimation = new ColorAnimation();
colorChangeAnimation.From = VariableColour;
colorChangeAnimation.To = BaseColour;
colorChangeAnimation.Duration = timeSpan;
PropertyPath colorTargetPath = new PropertyPath("(Panel.Background).(SolidColorBrush.Color)");
Storyboard CellBackgroundChangeStory = new Storyboard();
Storyboard.SetTarget(colorChangeAnimation, BackGroundCellGrid);
Storyboard.SetTargetProperty(colorChangeAnimation, colorTargetPath);
CellBackgroundChangeStory.Children.Add(colorChangeAnimation);
CellBackgroundChangeStory.Begin();
//VariableColour & BaseColour are class of Color, timeSpan is Class of TimeSpan, BackGroundCellGrid is class of Grid;
//no need to create SolidColorBrush and binding to it in XAML; //have fun!
Here is a set of attached properties that can be used if you run into issues with freezables.
using System;
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
public static class Blink
{
public static readonly DependencyProperty WhenProperty = DependencyProperty.RegisterAttached(
"When",
typeof(bool?),
typeof(Blink),
new PropertyMetadata(false, OnWhenChanged));
public static readonly DependencyProperty FromProperty = DependencyProperty.RegisterAttached(
"From",
typeof(Color),
typeof(Blink),
new FrameworkPropertyMetadata(Colors.Transparent, FrameworkPropertyMetadataOptions.Inherits));
public static readonly DependencyProperty ToProperty = DependencyProperty.RegisterAttached(
"To",
typeof(Color),
typeof(Blink),
new FrameworkPropertyMetadata(Colors.Orange, FrameworkPropertyMetadataOptions.Inherits));
public static readonly DependencyProperty PropertyProperty = DependencyProperty.RegisterAttached(
"Property",
typeof(DependencyProperty),
typeof(Blink),
new PropertyMetadata(default(DependencyProperty)));
public static readonly DependencyProperty DurationProperty = DependencyProperty.RegisterAttached(
"Duration",
typeof(Duration),
typeof(Blink),
new PropertyMetadata(new Duration(TimeSpan.FromSeconds(1))));
public static readonly DependencyProperty AutoReverseProperty = DependencyProperty.RegisterAttached(
"AutoReverse",
typeof(bool),
typeof(Blink),
new PropertyMetadata(true));
public static readonly DependencyProperty RepeatBehaviorProperty = DependencyProperty.RegisterAttached(
"RepeatBehavior",
typeof(RepeatBehavior),
typeof(Blink),
new PropertyMetadata(RepeatBehavior.Forever));
private static readonly DependencyProperty OldBrushProperty = DependencyProperty.RegisterAttached(
"OldBrush",
typeof(Brush),
typeof(Blink),
new PropertyMetadata(null));
public static void SetWhen(this UIElement element, bool? value)
{
element.SetValue(WhenProperty, value);
}
[AttachedPropertyBrowsableForChildren(IncludeDescendants = false)]
[AttachedPropertyBrowsableForType(typeof(UIElement))]
public static bool? GetWhen(this UIElement element)
{
return (bool?)element.GetValue(WhenProperty);
}
public static void SetFrom(this DependencyObject element, Color value)
{
element.SetValue(FromProperty, value);
}
[AttachedPropertyBrowsableForChildren(IncludeDescendants = false)]
[AttachedPropertyBrowsableForType(typeof(UIElement))]
public static Color GetFrom(this DependencyObject element)
{
return (Color)element.GetValue(FromProperty);
}
public static void SetTo(this DependencyObject element, Color value)
{
element.SetValue(ToProperty, value);
}
[AttachedPropertyBrowsableForChildren(IncludeDescendants = false)]
[AttachedPropertyBrowsableForType(typeof(UIElement))]
public static Color GetTo(this DependencyObject element)
{
return (Color)element.GetValue(ToProperty);
}
public static void SetProperty(this UIElement element, DependencyProperty value)
{
element.SetValue(PropertyProperty, value);
}
[AttachedPropertyBrowsableForChildren(IncludeDescendants = false)]
[AttachedPropertyBrowsableForType(typeof(UIElement))]
public static DependencyProperty GetProperty(this UIElement element)
{
return (DependencyProperty)element.GetValue(PropertyProperty);
}
public static void SetDuration(this UIElement element, Duration value)
{
element.SetValue(DurationProperty, value);
}
[AttachedPropertyBrowsableForChildren(IncludeDescendants = false)]
[AttachedPropertyBrowsableForType(typeof(UIElement))]
public static Duration GetDuration(this UIElement element)
{
return (Duration)element.GetValue(DurationProperty);
}
public static void SetAutoReverse(this UIElement element, bool value)
{
element.SetValue(AutoReverseProperty, value);
}
[AttachedPropertyBrowsableForChildren(IncludeDescendants = false)]
[AttachedPropertyBrowsableForType(typeof(UIElement))]
public static bool GetAutoReverse(this UIElement element)
{
return (bool)element.GetValue(AutoReverseProperty);
}
public static void SetRepeatBehavior(this UIElement element, RepeatBehavior value)
{
element.SetValue(RepeatBehaviorProperty, value);
}
[AttachedPropertyBrowsableForChildren(IncludeDescendants = false)]
[AttachedPropertyBrowsableForType(typeof(UIElement))]
public static RepeatBehavior GetRepeatBehavior(this UIElement element)
{
return (RepeatBehavior)element.GetValue(RepeatBehaviorProperty);
}
private static void OnWhenChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var property = GetProperty((UIElement)d) ?? GetDefaultProperty(d);
if (property == null || !typeof(Brush).IsAssignableFrom(property.PropertyType))
{
if (DesignerProperties.GetIsInDesignMode(d))
{
if (property != null)
{
throw new ArgumentException($"Could not blink for {d.GetType().Name}.{property.Name}", nameof(d));
}
}
return;
}
AnimateBlink(e.NewValue as bool?, (UIElement)d, property);
}
private static DependencyProperty GetDefaultProperty(DependencyObject d)
{
if (d is Control)
{
return Control.BackgroundProperty;
}
if (d is Panel)
{
return Panel.BackgroundProperty;
}
if (d is Border)
{
return Border.BackgroundProperty;
}
if (d is Shape)
{
return Shape.FillProperty;
}
if (DesignerProperties.GetIsInDesignMode(d))
{
throw new ArgumentException($"Could not find property to blink for {d.GetType().Name}", nameof(d));
}
return null;
}
private static void AnimateBlink(bool? blink, UIElement element, DependencyProperty property)
{
if (element == null)
{
return;
}
if (blink == true)
{
var brush = element.GetValue(property);
element.SetCurrentValue(OldBrushProperty, brush);
element.SetValue(property, Brushes.Transparent);
var from = element.GetFrom();
var to = element.GetTo();
var sb = new Storyboard();
var duration = element.GetDuration();
var animation = new ColorAnimation(from, to, duration)
{
AutoReverse = element.GetAutoReverse(),
RepeatBehavior = element.GetRepeatBehavior()
};
Storyboard.SetTarget(animation, element);
Storyboard.SetTargetProperty(animation, new PropertyPath($"{property.Name}.(SolidColorBrush.Color)"));
sb.Children.Add(animation);
sb.Begin();
}
else
{
var brush = element.GetValue(OldBrushProperty);
element.BeginAnimation(property, null);
element.SetCurrentValue(property, brush);
}
}
}
Usage:
<Grid>
<Grid.Resources>
<Style x:Key="BlinkWhenMouseOver"
TargetType="{x:Type Border}">
<Setter Property="local:Blink.When" Value="{Binding IsMouseOver, RelativeSource={RelativeSource Self}}" />
<Setter Property="local:Blink.From" Value="Honeydew" />
<Setter Property="local:Blink.To" Value="HotPink" />
<Setter Property="BorderThickness" Value="5" />
<Setter Property="local:Blink.Property" Value="{x:Static Border.BorderBrushProperty}" />
</Style>
</Grid.Resources>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Border Style="{StaticResource BlinkWhenMouseOver}" Background="Transparent"/>
<Border Grid.Row="1"
local:Blink.From="Aqua"
local:Blink.To="Yellow"
local:Blink.When="{Binding IsChecked,
ElementName=ToggleBlink}" />
<ToggleButton x:Name="ToggleBlink"
Grid.Row="2"
Content="Blink" />
</Grid>
In WPF ,Using animation maybe better. Expression blend have the relative animation/behaviour.
This post helped me. But if it is to change the color back after 1 second like the original question says,
ColorAnimation animation;
animation = new ColorAnimation();
animation.AutoReverse =true;
This worked well for me.
I have a path inside a button (it draws an "X"):
<Path x:Name="MyDeleteRowButton" Stroke="Gray" Grid.Row="0"
Data="M1,5 L11,15 M1,15 L11,5" StrokeThickness="2" HorizontalAlignment="Center"
Stretch="None"/>
On mouse over, I want the cross to go red, so I add:
<DataTemplate.Triggers>
<!-- Highlight row on mouse over, and highlight the delete button. -->
<EventTrigger RoutedEvent="ItemsControl.MouseEnter">
<EventTrigger.Actions>
<BeginStoryboard>
<Storyboard>
<!-- On mouse over, flicker the row to highlight it.-->
<DoubleAnimation
Storyboard.TargetProperty="Opacity"
From="0.5"
To="1"
Duration="0:0:0.250"
FillBehavior="Stop"/>
<!-- Highlight the delete button with red. -->
<ColorAnimation
To="Red"
Storyboard.TargetName="MyDeleteRowButton"
Storyboard.TargetProperty="(Stroke).(SolidColorBrush.Color)"
Duration="0:0:0.100"
FillBehavior="HoldEnd"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger.Actions>
</EventTrigger>
<!-- Grey out the delete button. -->
<EventTrigger RoutedEvent="ItemsControl.MouseLeave">
<EventTrigger.Actions>
<BeginStoryboard>
<Storyboard>
<!-- Space is precious: "delete" button appears only on a mouseover. -->
<ColorAnimation
To="Gray"
Storyboard.TargetName="MyDeleteRowButton"
Storyboard.TargetProperty="(Stroke).(SolidColorBrush.Color)"
Duration="0:0:0.100"
FillBehavior="HoldEnd"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger.Actions>
</EventTrigger>
</DataTemplate.Triggers>
The most confusing thing about this is the brackets within Storyboard.TargetProperty
. If you remove the brackets, nothing works.
For more information, see "propertyName Grammar" and "Storyboard.TargetProperty".
You can use DoubleAnimation
to change the color like this:
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Background"
From="0.0"
Duration="0:0:2"
To="1.0" />
</Storyboard>
Hope it helps