Wpf动画背景颜色

64

我需要帮助做出正确的决定。当发生某些事件时,我需要给我的用户控件添加一个背景颜色动画。当它发生时,我想改变背景颜色1秒钟,然后再恢复原来的样子。我应该怎么做?是使用颜色动画还是计时器或其他方式?

问题已解决。感谢大家!这对我很有用:

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

问题非常开放,因为它更倾向于个人选择。我建议重新措辞问题或包括您尝试过的事情。 - Backlash
很抱歉,我添加了一个“animate”标签,看起来它被自动更改了。 - Taras
你至少应该发布一些代码/更多信息(哪个事件?你的XAML长什么样?) - Florian Gl
8个回答

94

我会使用一个带有ColorAnimationEventTrigger

在这个例子中,当鼠标离开时,按钮背景变成绿色。这段代码可能与您所需要的代码类似。

<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
我得到了无效的XAML,应该将Duration改为0:0:1吗? - Branden Barber
23
@Klaus78,(Button.Background).(SolidColorBrush.Color) 是什么意思?这里的括号起到了什么作用? - Helic
2
@Helic 我自己也问过这个问题。MSDN 表示它是一个 Single Property, Attached or Otherwise Type-Qualified 声明,请参见 https://learn.microsoft.com/en-us/dotnet/framework/wpf/advanced/propertypath-xaml-syntax#single-property-attached-or-otherwise-type-qualified。 - ViRuSTriNiTy
这是fill属性的最佳选择。非常感谢。 - Ibrahim Kais Ibrahim
尝试在TargetProperty上执行此操作:Storyboard.TargetProperty="(Control.Background).(SolidColorBrush.Color)",将Button.Background更改为Control.Background。它具有相同的效果。 - TERIHAX

33

注意,如果您的背景是一个冻结的实例,则可能会收到System.InvalidOperationException。

无法在“System.Windows.Media.SolidColorBrush”上动画显示“Color”属性,因为对象已封闭或冻结。

要纠正此消息,请将控件的背景分配给非冻结实例。

// Do not use a frozen instance
this.elGrid.Background = new SolidColorBrush(Colors.Orange);
this.elGrid.Background.BeginAnimation(SolidColorBrush.ColorProperty, animation);

MSDN 上的可冻结对象概述


1
这应该是添加到原问题的代码中。 - fbmd

10
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!

2
以下是一组附加属性,可以在你遇到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);
        }
    }
}

使用方法:

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

0

这篇文章对我很有帮助。但是,如果要像原问题所说的那样在1秒后将颜色改回来,

 ColorAnimation animation;
    animation = new ColorAnimation();
    animation.AutoReverse =true;

0

这对我很有效。

我有一个在按钮内的路径(它绘制一个“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"/>

当鼠标悬停时,我希望十字变成红色,因此我添加:

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

最令人困惑的是 Storyboard.TargetProperty 中的括号。如果去掉括号,什么都不起作用。

更多信息,请参见 "属性名称语法" 和 "Storyboard.TargetProperty"。


-1
在WPF中,使用动画可能更好。Expression Blend具有相关的动画/行为。

-9

您可以使用DoubleAnimation来改变颜色,如下所示:

<Storyboard>
    <DoubleAnimation Storyboard.TargetProperty="Background"
                            From="0.0"
                            Duration="0:0:2"
                            To="1.0" />                        
</Storyboard>

希望它有所帮助。


3
实际上,Background 是一个笔刷类型。DoubleAnimation 被用来对双精度浮点数类型进行动画化。 - Klaus78
这是非常正确的,Kaus78。我从之前为Label控件定义的ControlTemplate中提取了这个片段,并将Opacity替换为Background,只是为了快速举例。 - Iftikhar

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接