WPF Fade Animation

2019-05-13 12:18发布

我怎么会做出/控制淡出时,它变得可见。

下面是我的失败尝试:

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

Answer 1:

我不知道如何在纯XAML一举两得动画(淡入和淡出)。 但是简单的淡出就可以实现相对简单。 与触发器替换DataTriggers,并删除ExitActions,因为他们是没有意义的淡出场景。 这是你拥有的一切:

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

但是,嘿,不要放弃。 如果你想支持动画我可以建议XAML后面小的编码。 我们做了一招后,我们会得到你想要的东西通过添加一行代码在XAML:

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

每当我们改变btn.Visibility从可见光到隐藏/折叠按钮时会淡出。 我们每次改变时间能见度背面的按钮将淡出。这一招将与任何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()方法。 正如你所看到的衰落动画完成后,直到我们不允许更改此属性。

这个代码是没有线程安全也不无bug。 它唯一的目的是显示方向:)。 所以随意提高,编辑并获得声誉)。



Answer 2:

你不能直接使用Visibility属性的淡出,因为设置一个触发上会先隐藏/关闭的控制,那么它的动画。 所以基本上,你会得到一个倒塌的控制=>没有动画。

一个“可靠”的办法是引入新的依赖项属性(连接或没有),说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>


Answer 3:

我意识到这个问题是有点老了,但我只现在已经读了它,我已经调整了由Anvaka给出的代码。 它支持结合可视性(仅当装订模式设置为双向)。 它还支持淡入淡出和2个不同的时间长度值。

这里是类:

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


Answer 4:

我已经来这一个稍微不同的方式-我有雷人的答案的扩展版本, 这个问题增加了一个淡入()和淡出()扩展方法坍塌或显示元素作为适当的一切,然后代替使物体的可见我可以叫淡入()和淡出()在他们 - 它会工作的任何元素没有任何具体的动画代码。

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


Answer 5:

这是用行为做最好

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

这是专门被应用到边界 - 我还没有尝试过的用户控件,但我希望这同样适用。

要使用它,你需要的交融互动的命名空间:

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>

你需要在命名空间中添加对动作类太..



Answer 6:

很老了,但你能不能只链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>


Answer 7:

You may want to try AutoReverse property ... though I am not sure if it works the way you want it to. This is what I found on MSDN :

When a timeline's AutoReverse property is set to true and its RepeatBehavior property causes it to repeat, each forward iteration is followed by a backward iteration. This makes one repetition. For example, a timeline with an AutoReverse value of true with an iteration Count of 2 would play forward once, then backwards, then forwards again, and then backwards again.



文章来源: WPF Fade Animation