Wpf animate background color

2019-01-11 06:10发布

问题:

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);

回答1:

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>


回答2:

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



回答3:

        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!



回答4:

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>


回答5:

In WPF ,Using animation maybe better. Expression blend have the relative animation/behaviour.



回答6:

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;


回答7:

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".



回答8:

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