WPF控件的淡出效果

22
在我的 WPF 应用程序中,我有一个反馈控件,希望在用户完成某个操作(保存数据、删除等)后出现。初始时,可见性设置为 Hidden,样式设置为 animateFadeOut 样式,该样式已定义为资源(参见下面)。然后,在我的 C# 代码中,我想设置文本和控件可见性为 visible,并让反馈控件显示消息,然后在 5 秒后淡出并保持隐藏(Visibility.Hidden)。
以下 XAML 在我第一次调用 control.Visibility = Visibility.Visible 时有效,但是控件第二次不会重新出现。我认为这是因为动画仍在运行,它控制着反馈控件。我尝试将 FillBehavior 设置为 "Stop",但那只会使控件再次可见,而我想要它隐藏。然后,使用 FillBehavior="Stop",我尝试设置触发器 "当 Opacity=0 时,将可见性设置为 Hidden"。但是触发器似乎没有触发,当动画完成后,我又再次看到了可见的控件。
请帮忙指出我在这里做错了什么。
或者,如果您可以建议一种更好的方法来显示一个控件,在 5 秒后淡出并能够重复调用,我会非常感激。
谢谢!
<Style TargetType="{x:Type FrameworkElement}" x:Key="animateFadeOut">
        <Style.Triggers>
            <Trigger Property="Visibility" Value="Visible">
                <Trigger.EnterActions>
                    <BeginStoryboard >
                        <Storyboard>
                            <DoubleAnimation BeginTime="0:0:5.0" Storyboard.TargetProperty="Opacity"
                         From="1.0" To="0.0" Duration="0:0:0.5"/>
                        </Storyboard>
                    </BeginStoryboard>             
                </Trigger.EnterActions>
            </Trigger>
        </Style.Triggers> 
    </Style>

我采用了稍微不同的方法来解决这个问题...... 我用C#编写了它。(请参见下面的帖子-评论部分对于代码帖子来说太小了) - sondlerd
6个回答

36
问题在于,当你的动画完成后,控件仍然具有可见性(Visibility=Visible),因此无法再次进入。
我宁愿使用整体完成的动画,首先显示控件,然后隐藏它。
<Storyboard x:Key="animate">
    <ObjectAnimationUsingKeyFrames BeginTime="0:0:0" Storyboard.TargetProperty="Visibility">
        <DiscreteObjectKeyFrame KeyTime="0">
            <DiscreteObjectKeyFrame.Value>
                <Visibility>Visible</Visibility>
            </DiscreteObjectKeyFrame.Value>
        </DiscreteObjectKeyFrame>
    </ObjectAnimationUsingKeyFrames>
    <DoubleAnimation BeginTime="0:0:0.0" Storyboard.TargetProperty="Opacity" From="0" To="1" Duration="0:0:0.2"/>
    <DoubleAnimation BeginTime="0:0:5.0" Storyboard.TargetProperty="Opacity" From="1" To="0" Duration="0:0:0.5"/>
    <ObjectAnimationUsingKeyFrames BeginTime="0:0:5.5" Storyboard.TargetProperty="Visibility">
        <DiscreteObjectKeyFrame KeyTime="0">
            <DiscreteObjectKeyFrame.Value>
                <Visibility>Hidden</Visibility>
            </DiscreteObjectKeyFrame.Value>
        </DiscreteObjectKeyFrame>
    </ObjectAnimationUsingKeyFrames>
</Storyboard>

然后按如下方式使用:

((Storyboard)FindResource("animate")).Begin(someControl);

谢谢,Alpha-Mouse。你回答了我在Liz下面发布的问题。另一个问题是,动画完成后,不透明度是否为0.0,还是只在动画中?我问这个问题是因为当我将FillBehavior设置为Stop时,不透明度返回到1.0。 - sondlerd
@sondlerd:当FillBehaviour为Stop时,是的,属性值将返回到其原始值。 (http://msdn.microsoft.com/en-us/library/system.windows.media.animation.fillbehavior.aspx) - alpha-mouse
你可以将两个关键帧合并到同一个动画中,并在内联定义枚举 <DiscreteObjectKeyFrame KeyTime="0:0:6.5" Value="{x:Static Visibility.Hidden}"/>,从而缩短代码长度并提高效率。 - Itzalive
当我开始寻找这个问题的答案时,我发现了很多不完整的回答和错误的代码。因此,我非常感谢这个优雅的解决方案。谢谢 :) - dcreight

17

Liz正确指出Visibility仍然是Visible。alpha-mouse也正确,你需要在某些时候将其设置回Hidden。但是如果你在动画完成之前将它设置回去,它是不起作用的,像这样:

MyControl.Visibility = System.Windows.Visibility.Visible;
MyControl.Visibility = System.Windows.Visibility.Hidden;

因为动画具有更高的优先级 (MSDN)

您可以在 Storyboard.Completed 事件中将其设置回 Hidden:

private void Show()
    {
        MyControl.Visibility = System.Windows.Visibility.Visible;

        var a = new DoubleAnimation
                    {
                        From = 1.0,
                        To = 0.0,
                        FillBehavior= FillBehavior.Stop,
                        BeginTime = TimeSpan.FromSeconds(2),
                        Duration = new Duration(TimeSpan.FromSeconds(0.5))
                    };
        var storyboard = new Storyboard();

        storyboard.Children.Add(a);
        Storyboard.SetTarget(a, MyControl);
        Storyboard.SetTargetProperty(a, new PropertyPath(OpacityProperty));
        storyboard.Completed += delegate { MyControl.Visibility = System.Windows.Visibility.Hidden; };
        storyboard.Begin();            
    }

谢谢Kai,我似乎总是回到C#来完成WPF中的任务。我对WPF相对较新(2007年工作了6个月,然后在过去的三周里为一个项目工作)。也许这只是学习曲线。 感谢您指出Storyboard.Completed事件。 - sondlerd
只要能完成任务,我不介意用代码来实现。事实上,在这种情况下,我认为用代码实现比使用XAML更清晰。 - treehouse

12

这是我的解决方法。它可以将控件淡入淡出,不需要调整可见性,只需通过更改透明度来实现。

感谢这篇文章中Kane的原始代码:使用WPF动画淡化任何控件

Storyboard storyboard = new Storyboard();
TimeSpan duration = TimeSpan.FromMilliseconds(500); //

DoubleAnimation fadeInAnimation = new DoubleAnimation() 
    { From = 0.0, To = 1.0, Duration = new Duration(duration) };

DoubleAnimation fadeOutAnimation = new DoubleAnimation()
    { From = 1.0, To = 0.0, Duration = new Duration(duration) };
fadeOutAnimation.BeginTime = TimeSpan.FromSeconds(5);

Storyboard.SetTargetName(fadeInAnimation, element.Name);
Storyboard.SetTargetProperty(fadeInAnimation, new PropertyPath("Opacity", 1));
storyboard.Children.Add(fadeInAnimation);
storyboard.Begin(element);

Storyboard.SetTargetName(fadeOutAnimation, element.Name);
Storyboard.SetTargetProperty(fadeOutAnimation, new PropertyPath("Opacity", 0));
storyboard.Children.Add(fadeOutAnimation);
storyboard.Begin(element);

1
不错的例子。我尝试了不同的淡入/淡出动画解决方案,这个非常简单明了。 - Dragon

3

我的天啊,这花了好长时间。看看这个,它解决了在可见性更改为“可见”和“隐藏”时使用alpha进行动画并且动画不会冻结的问题。

using System.Windows;

namespace WpfApplication4
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, RoutedEventArgs e)
        {
            button.Visibility = Visibility.Hidden;
        }

        private void button2_Click(object sender, RoutedEventArgs e)
        {
            button.Visibility = Visibility.Visible;
        }
    }
}

XAML:
<Window x:Class="WpfApplication4.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfApplication4"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <Grid.Resources>

            <Style BasedOn="{StaticResource {x:Type Button}}" TargetType="{x:Type Button}">
                <Style.Resources>
                    <Storyboard x:Key="FadeOut">
                        <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Visibility" FillBehavior="Stop">
                            <DiscreteObjectKeyFrame KeyTime="0:0:0" Value="{x:Static Visibility.Visible}"/>
                            <DiscreteObjectKeyFrame KeyTime="0:0:1" Value="{x:Static Visibility.Hidden}"/>
                        </ObjectAnimationUsingKeyFrames>
                        <DoubleAnimation Storyboard.TargetProperty="Opacity" From="1" To="0" Duration="0:0:1" AutoReverse="False" />
                    </Storyboard>
                    <Storyboard x:Key="FadeIn">
                        <DoubleAnimation Storyboard.TargetProperty="Opacity" From="0" To="1" Duration="0:0:1" AutoReverse="False" />
                    </Storyboard>
                </Style.Resources>
                <Setter Property="Width" Value="120"></Setter>
                <Style.Triggers>
                    <MultiTrigger>
                        <MultiTrigger.Conditions>
                            <Condition Property="Visibility" Value="Hidden" />
                            <Condition Property="Opacity" Value="1" />
                        </MultiTrigger.Conditions>
                        <MultiTrigger.EnterActions>
                            <StopStoryboard BeginStoryboardName="FadeInStoryboard" />
                            <BeginStoryboard Name="FadeOutStoryboard" Storyboard="{StaticResource FadeOut}" />
                        </MultiTrigger.EnterActions>
                    </MultiTrigger>
                    <MultiTrigger>
                        <MultiTrigger.Conditions>
                            <Condition Property="Visibility" Value="Visible" />
                            <Condition Property="Opacity" Value="0" />
                        </MultiTrigger.Conditions>
                        <MultiTrigger.EnterActions>
                            <StopStoryboard BeginStoryboardName="FadeOutStoryboard" />
                            <BeginStoryboard Name="FadeInStoryboard" Storyboard="{StaticResource FadeIn}" />
                        </MultiTrigger.EnterActions>
                    </MultiTrigger>
                </Style.Triggers>
            </Style>

        </Grid.Resources>
        <Button x:Name="button" Content="Button" HorizontalAlignment="Left" Margin="200,186,0,0" VerticalAlignment="Top" Width="75" Height="38" />
        <Button x:Name="button1" Content="Hide it" HorizontalAlignment="Left" Margin="112,96,0,0" VerticalAlignment="Top" Width="75" Click="button1_Click"/>
        <Button x:Name="button2" Content="Show it" HorizontalAlignment="Left" Margin="200,96,0,0" VerticalAlignment="Top" Width="75" Click="button2_Click"/>
        <Label x:Name="label" Content="{Binding ElementName=button, Path=Opacity}" HorizontalAlignment="Left" Margin="10,10,0,0" VerticalAlignment="Top"/>
        <Label x:Name="label1" Content="{Binding ElementName=button, Path=Visibility}" HorizontalAlignment="Left" Margin="10,36,0,0" VerticalAlignment="Top"/>

    </Grid>
</Window>

2
所有上面的答案都使用了至少一些XAML代码,我个人并不太喜欢它(因为它很难理解),所以我找到了一种用简单的C#代码实现相同效果的方法:
int secs = 2; // How long the fade should take in seconds

for (int i = 99; i >= 0; i--)
{
    someControl.Opacity = i / 100d;

    await Task.Delay(secs * 10);
}

someControl.Visibility = Visibility.Hidden;

someControl.Opacity = 1;

您可以在任何Control上使用此功能。您还需要在方法签名中添加async修饰符。如果没有await操作符,当淡出控件时,您的UI无法响应。
在控件淡出后,您可以使用以下方式使其再次可见:
someControl.Visibility = Visibility.Visible;

这种方法可能不是“最佳”的,但它肯定是最简单和易于理解的。


0

这应该可以修复你的故事板。

然而,记住一旦动画完成,你的控件将完全不透明 - 不可见,但是你的可见性属性仍然设置为可见。因此,你需要确保在某个地方将可见性属性重置为隐藏或折叠。

<Style TargetType="{x:Type FrameworkElement}" x:Key="animateFadeOut">
         <Style.Triggers>
            <Trigger Property="Visibility" Value="Visible">
               <Trigger.EnterActions>
                  <BeginStoryboard Name="MyFadeEffect">
                     <Storyboard>
                        <DoubleAnimation BeginTime="0:0:5.0" Storyboard.TargetProperty="Opacity"
                         From="1.0" To="0.0" Duration="0:0:0.5"/>
                     </Storyboard>
                  </BeginStoryboard>
               </Trigger.EnterActions>
               <Trigger.ExitActions>
                  <StopStoryboard BeginStoryboardName="MyFadeEffect"/>
               </Trigger.ExitActions>
            </Trigger>
         </Style.Triggers>
      </Style>

谢谢您的快速回复。我能否在故事板结束时将可见性设置为隐藏,以解决您提出的问题,“您的可见性属性仍然设置为可见”? - sondlerd
不,我试过了,但是你不能改变作为触发器条件的属性值。但现在你知道如何停止动画了。 :) - Liz

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