WPF淡出animation

当它变为可见时,我将如何使控制淡入/淡出。

以下是我的失败尝试:

<Window x:Class="WadFileTester.Form1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Name="MyWindow" Title="WAD File SI Checker" Height="386" Width="563" WindowStyle="SingleBorderWindow" DragEnter="Window_DragEnter" DragLeave="Window_DragLeave" DragOver="Window_DragOver" Drop="Window_Drop" AllowDrop="True"> <Window.Resources> <Style TargetType="ListView" x:Key="animatedList"> <Style.Triggers> <DataTrigger Binding="{Binding Visibility}" Value="Visible"> <DataTrigger.EnterActions> <BeginStoryboard> <Storyboard> <DoubleAnimation Storyboard.TargetProperty="Opacity" From="0.0" To="1.0" Duration="0:0:5" /> </Storyboard> </BeginStoryboard> </DataTrigger.EnterActions> <DataTrigger.ExitActions> <BeginStoryboard> <Storyboard> <DoubleAnimation Storyboard.TargetProperty="Opacity" From="1.0" To="0.0" Duration="0:0:5" /> </Storyboard> </BeginStoryboard> </DataTrigger.ExitActions> </DataTrigger> </Style.Triggers> </Style> </Window.Resources> <Grid> <ListView Name="listView1" Style="{StaticResource animatedList}" TabIndex="1" Margin="12,41,12,12" Visibility="Hidden"> </ListView> </Grid> </Window> 

我不知道如何在纯XAML中同时执行两个animation(淡入和淡出)。 但是简单的淡出可以实现得比较简单。 用触发器replaceDataTriggers,并删除ExitActions,因为它们在淡出scheme中没有意义。 这是你将会有的:

  <Style TargetType="FrameworkElement" x:Key="animatedList"> <Setter Property="Visibility" Value="Hidden"/> <Style.Triggers> <Trigger Property="Visibility" Value="Visible"> <Trigger.EnterActions> <BeginStoryboard> <Storyboard> <DoubleAnimation Storyboard.TargetProperty="Opacity" From="0.0" To="1.0" Duration="0:0:0.2"/> </Storyboard> </BeginStoryboard> </Trigger.EnterActions> </Trigger> </Style.Triggers> </Style> 

但是,嘿,不要放弃。 如果你想支持这两个animation,我可以build议在XAML后面编码。 在我们做了一个技巧之后,我们将通过在XAML中添加一行代码来获得您想要的结果:

 <Button Content="Fading button" x:Name="btn" loc:VisibilityAnimation.IsActive="True"/> 

每次我们改变btn.Visibility从Visible到Hidden / Collapsedbutton都会淡出。 每次我们改变可见性时,button都会淡入。这个技巧可以用于任何FrameworkElement(包括ListView :))。

这里是VisibilityAnimation.IsActive附加属性的代码:

  public class VisibilityAnimation : DependencyObject { private const int DURATION_MS = 200; private static readonly Hashtable _hookedElements = new Hashtable(); public static readonly DependencyProperty IsActiveProperty = DependencyProperty.RegisterAttached("IsActive", typeof(bool), typeof(VisibilityAnimation), new FrameworkPropertyMetadata(false, new PropertyChangedCallback(OnIsActivePropertyChanged))); public static bool GetIsActive(UIElement element) { if (element == null) { throw new ArgumentNullException("element"); } return (bool)element.GetValue(IsActiveProperty); } public static void SetIsActive(UIElement element, bool value) { if (element == null) { throw new ArgumentNullException("element"); } element.SetValue(IsActiveProperty, value); } static VisibilityAnimation() { UIElement.VisibilityProperty.AddOwner(typeof(FrameworkElement), new FrameworkPropertyMetadata(Visibility.Visible, new PropertyChangedCallback(VisibilityChanged), CoerceVisibility)); } private static void VisibilityChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { // So what? Ignore. } private static void OnIsActivePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { var fe = d as FrameworkElement; if (fe == null) { return; } if (GetIsActive(fe)) { HookVisibilityChanges(fe); } else { UnHookVisibilityChanges(fe); } } private static void UnHookVisibilityChanges(FrameworkElement fe) { if (_hookedElements.Contains(fe)) { _hookedElements.Remove(fe); } } private static void HookVisibilityChanges(FrameworkElement fe) { _hookedElements.Add(fe, false); } private static object CoerceVisibility(DependencyObject d, object baseValue) { var fe = d as FrameworkElement; if (fe == null) { return baseValue; } if (CheckAndUpdateAnimationStartedFlag(fe)) { return baseValue; } // If we get here, it means we have to start fade in or fade out // animation. In any case return value of this method will be // Visibility.Visible. var visibility = (Visibility)baseValue; var da = new DoubleAnimation { Duration = new Duration(TimeSpan.FromMilliseconds(DURATION_MS)) }; da.Completed += (o, e) => { // This will trigger value coercion again // but CheckAndUpdateAnimationStartedFlag() function will reture true // this time, and animation will not be triggered. fe.Visibility = visibility; // NB: Small problem here. This may and probably will brake // binding to visibility property. }; if (visibility == Visibility.Collapsed || visibility == Visibility.Hidden) { da.From = 1.0; da.To = 0.0; } else { da.From = 0.0; da.To = 1.0; } fe.BeginAnimation(UIElement.OpacityProperty, da); return Visibility.Visible; } private static bool CheckAndUpdateAnimationStartedFlag(FrameworkElement fe) { var hookedElement = _hookedElements.Contains(fe); if (!hookedElement) { return true; // don't need to animate unhooked elements. } var animationStarted = (bool) _hookedElements[fe]; _hookedElements[fe] = !animationStarted; return animationStarted; } } 

这里最重要的是CoerceVisibility()方法。 正如你所看到的,我们不允许改变这个属性,直到淡入淡出的animation完成。

此代码既不是线程安全的也没有缺陷。 它唯一的意图是显示方向:)。 所以,随意改善,编辑和获得声誉;)。

您不能直接使用Visibility属性进行淡出,因为在其上设置触发器将首先隐藏/折叠控件,然后为其添加animation。 所以基本上你会得到一个崩溃的控制animation=>什么都没有。

一个“可靠”的方法是引入一个新的依赖属性(附加与否),说IsOpen并设置一个属性触发器IsOpen=True与:

EnterAction:

  • 确保可见性设置为可见
  • 淡入淡出从0到1

ExitAction:

  • 可见性设置为在关键帧0处可见,在最后一个关键帧处折叠/隐藏
  • 从1到0淡出不透明度。

这是一个例子:

 <Style TargetType="{x:Type local:TCMenu}"> <Style.Resources> <Storyboard x:Key="FadeInMenu"> <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Opacity)" Storyboard.TargetName="{x:Null}"> <EasingDoubleKeyFrame KeyTime="0" Value="0"/> <EasingDoubleKeyFrame KeyTime="0:0:0.2" Value="1"/> </DoubleAnimationUsingKeyFrames> <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Visibility)" Storyboard.TargetName="{x:Null}"> <DiscreteObjectKeyFrame KeyTime="0:0:0.0" Value="{x:Static Visibility.Visible}"/> </ObjectAnimationUsingKeyFrames> </Storyboard> <Storyboard x:Key="FadeOutMenu"> <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Opacity)" Storyboard.TargetName="{x:Null}"> <EasingDoubleKeyFrame KeyTime="0" Value="1"/> <EasingDoubleKeyFrame KeyTime="0:0:0.2" Value="0"/> </DoubleAnimationUsingKeyFrames> <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Visibility)" Storyboard.TargetName="{x:Null}"> <DiscreteObjectKeyFrame KeyTime="0:0:0.0" Value="{x:Static Visibility.Visible}"/> <DiscreteObjectKeyFrame KeyTime="0:0:0.2" Value="{x:Static Visibility.Collapsed}"/> </ObjectAnimationUsingKeyFrames> </Storyboard> </Style.Resources> <Style.Triggers> <Trigger Property="IsOpen" Value="true"> <Trigger.EnterActions> <BeginStoryboard Storyboard="{StaticResource FadeInMenu}"/> </Trigger.EnterActions> <Trigger.ExitActions> <BeginStoryboard Storyboard="{StaticResource FadeOutMenu}"/> </Trigger.ExitActions> </Trigger> </Style.Triggers> <Setter Property="Visibility" Value="Collapsed" /> </Style> 

我意识到这个问题是有点老,但我现在只读了,我已经调整了由Anvaka给出的代码。 它支持绑定到可视性(仅当绑定模式设置为双向时)。 它也支持FadeIn和FadeOut的2个不同的持续时间值。

这是class级:

  public class VisibilityAnimation : DependencyObject { #region Private Variables private static HashSet<UIElement> HookedElements = new HashSet<UIElement>(); private static DoubleAnimation FadeAnimation = new DoubleAnimation(); private static bool SurpressEvent; private static bool Running; #endregion #region Attached Dependencies public static readonly DependencyProperty IsActiveProperty = DependencyProperty.RegisterAttached("IsActive", typeof(bool), typeof(VisibilityAnimation), new FrameworkPropertyMetadata(false, new PropertyChangedCallback(OnIsActivePropertyChanged))); public static bool GetIsActive(UIElement element) { if (element == null) throw new ArgumentNullException("element"); return (bool)element.GetValue(IsActiveProperty); } public static void SetIsActive(UIElement element, bool value) { if (element == null) throw new ArgumentNullException("element"); element.SetValue(IsActiveProperty, value); } public static readonly DependencyProperty FadeInDurationProperty = DependencyProperty.RegisterAttached("FadeInDuration", typeof(double), typeof(VisibilityAnimation), new PropertyMetadata(0.5)); public static double GetFadeInDuration(UIElement e) { if (e == null) throw new ArgumentNullException("element"); return (double)e.GetValue(FadeInDurationProperty); } public static void SetFadeInDuration(UIElement e, double value) { if (e == null) throw new ArgumentNullException("element"); e.SetValue(FadeInDurationProperty, value); } public static readonly DependencyProperty FadeOutDurationProperty = DependencyProperty.RegisterAttached("FadeOutDuration", typeof(double), typeof(VisibilityAnimation), new PropertyMetadata(1.0)); public static double GetFadeOutDuration(UIElement e) { if (e == null) throw new ArgumentNullException("element"); return (double)e.GetValue(FadeOutDurationProperty); } public static void SetFadeOutDuration(UIElement e, double value) { if (e == null) throw new ArgumentNullException("element"); e.SetValue(FadeOutDurationProperty, value); } #endregion #region Callbacks private static void VisibilityChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { // So what? Ignore. // We only specified a property changed call-back to be able to set a coercion call-back } private static void OnIsActivePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { // Get the framework element and leave if it is null var fe = d as FrameworkElement; if (fe == null) return; // Hook the element if IsActive is true and unhook the element if it is false if (GetIsActive(fe)) HookedElements.Add(fe); else HookedElements.Remove(fe); } private static object CoerceVisibility(DependencyObject d, object baseValue) { if (SurpressEvent) return baseValue; // Ignore coercion if we set the SurpressEvent flag var FE = d as FrameworkElement; if (FE == null || !HookedElements.Contains(FE)) return baseValue; // Leave if the element is null or does not belong to our list of hooked elements Running = true; // Set the running flag so that an animation does not change the visibility if another animation was started (Changing Visibility before the 1st animation completed) // If we get here, it means we have to start fade in or fade out animation // In any case return value of this method will be Visibility.Visible Visibility NewValue = (Visibility)baseValue; // Get the new value if (NewValue == Visibility.Visible) FadeAnimation.Duration = new Duration(TimeSpan.FromSeconds((double)d.GetValue(FadeInDurationProperty))); // Get the duration that was set for fade in else FadeAnimation.Duration = new Duration(TimeSpan.FromSeconds((double)d.GetValue(FadeOutDurationProperty))); // Get the duration that was set for fade out // Use an anonymous method to set the Visibility to the new value after the animation completed FadeAnimation.Completed += (obj, args) => { if (FE.Visibility != NewValue && !Running) { SurpressEvent = true; // SuppressEvent flag to skip coercion FE.Visibility = NewValue; SurpressEvent = false; Running = false; // Animation and Visibility change is now complete } }; FadeAnimation.To = (NewValue == Visibility.Collapsed || NewValue == Visibility.Hidden) ? 0 : 1; // Set the to value based on Visibility FE.BeginAnimation(UIElement.OpacityProperty, FadeAnimation); // Start the animation (it will only start after we leave the coercion method) return Visibility.Visible; // We need to return Visible in order to see the fading take place, otherwise it just sets it to Collapsed/Hidden without showing the animation } #endregion static VisibilityAnimation() { // Listen for visibility changes on all elements UIElement.VisibilityProperty.AddOwner(typeof(FrameworkElement), new FrameworkPropertyMetadata(Visibility.Visible, new PropertyChangedCallback(VisibilityChanged), CoerceVisibility)); } } 

我有一个稍微不同的方式 – 我有一个扩展版本的Ray的这个问题的答案,它添加了一个FadeIn()和FadeOut()扩展方法的一切崩溃或显示元素适当,然后改为使对象可见,我可以在它们上调用FadeIn()和FadeOut(),并且它可以处理任何没有任何特定animation代码的元素。

  public static T FadeFromTo(this UIElement uiElement, double fromOpacity, double toOpacity, int durationInMilliseconds, bool loopAnimation, bool showOnStart, bool collapseOnFinish) { var timeSpan = TimeSpan.FromMilliseconds(durationInMilliseconds); var doubleAnimation = new DoubleAnimation(fromOpacity, toOpacity, new Duration(timeSpan)); if (loopAnimation) doubleAnimation.RepeatBehavior = RepeatBehavior.Forever; uiElement.BeginAnimation(UIElement.OpacityProperty, doubleAnimation); if (showOnStart) { uiElement.ApplyAnimationClock(UIElement.VisibilityProperty, null); uiElement.Visibility = Visibility.Visible; } if (collapseOnFinish) { var keyAnimation = new ObjectAnimationUsingKeyFrames{Duration = new Duration(timeSpan) }; keyAnimation.KeyFrames.Add(new DiscreteObjectKeyFrame(Visibility.Collapsed, KeyTime.FromTimeSpan(timeSpan))); uiElement.BeginAnimation(UIElement.VisibilityProperty, keyAnimation); } return uiElement; } public static T FadeIn(this UIElement uiElement, int durationInMilliseconds) { return uiElement.FadeFromTo(0, 1, durationInMilliseconds, false, true, false); } public static T FadeOut(this UIElement uiElement, int durationInMilliseconds) { return uiElement.FadeFromTo(1, 0, durationInMilliseconds, false, false, true); } 

这最好用一个行为来完成

 class AnimatedVisibilityFadeBehavior : Behavior<Border> { public Duration AnimationDuration { get; set; } public Visibility InitialState { get; set; } DoubleAnimation m_animationOut; DoubleAnimation m_animationIn; protected override void OnAttached() { base.OnAttached(); m_animationIn = new DoubleAnimation(1, AnimationDuration, FillBehavior.HoldEnd); m_animationOut = new DoubleAnimation(0, AnimationDuration, FillBehavior.HoldEnd); m_animationOut.Completed += (sender, args) => { AssociatedObject.SetCurrentValue(Border.VisibilityProperty, Visibility.Collapsed); }; AssociatedObject.SetCurrentValue(Border.VisibilityProperty, InitialState == Visibility.Collapsed ? Visibility.Collapsed : Visibility.Visible); Binding.AddTargetUpdatedHandler(AssociatedObject, Updated); } private void Updated(object sender, DataTransferEventArgs e) { var value = (Visibility)AssociatedObject.GetValue(Border.VisibilityProperty); switch (value) { case Visibility.Collapsed: AssociatedObject.SetCurrentValue(Border.VisibilityProperty, Visibility.Visible); AssociatedObject.BeginAnimation(Border.OpacityProperty, m_animationOut); break; case Visibility.Visible: AssociatedObject.BeginAnimation(Border.OpacityProperty, m_animationIn); break; } } } 

这是专门应用于边界 – 我还没有尝试用户控件,但我期望同样适用。

要使用它,你需要Blend Interactivity命名空间:

 xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity" 

并在你想要的行为的边界上使用这个标记:

 <i:Interaction.Behaviors> <Interactivity:AnimatedVisibilityFadeBehavior AnimationDuration="0:0:0.3" InitialState="Collapsed" /> </i:Interaction.Behaviors> 

您还需要在行为类的名称空间中添加..

现在已经很久了,但是你能不能连锁DoubleAnimations?

 <DataTrigger.EnterActions> <BeginStoryboard> <Storyboard> <DoubleAnimation Storyboard.TargetProperty="Opacity" From="0.0" To="1.0" Duration="0:0:5" /> <DoubleAnimation Storyboard.TargetProperty="Opacity" From="1.0" To="0.0" Duration="0:0:5" /> </Storyboard> </BeginStoryboard> </DataTrigger.EnterActions> 

您可能想要尝试AutoReverse属性…虽然我不确定它是否按照您希望的方式工作。 这是我在MSDN上find的:

当时间轴的AutoReverse属性设置为true并且其RepeatBehavior属性导致它重复时,每个前向迭代之后是后向迭代。 这使得一个重复。 例如,一个具有AutoReverse值为true且时间线迭代计数为2的时间线将向前播放一次,然后向后播放,然后再向前播放,然后再向后播放。