[ACCEPTED]-WPF Fade Animation-animation

Accepted answer
Score: 55

I don't know how to do both animations (fade 23 in and fade out) in pure XAML. But simple 22 fade out can be achieved relatively simple. Replace 21 DataTriggers with Triggers, and remove ExitActions 20 since they makes no sense in Fade out scenario. This 19 is what you will have:

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

But hey, don't give 18 up. If you want to support both animations 17 I can suggest small coding behind the XAML. After 16 we do a trick, we will get what you want 15 by adding one line of code in XAML:

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

Every 14 time we change btn.Visibility from Visible 13 to Hidden/Collapsed button will fade out. And 12 every time we change Visibility back the 11 button will fade in. This trick will work 10 with any FrameworkElement (including ListView 9 :) ).

Here is the code of VisibilityAnimation.IsActive 8 attached property:

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

The most important thing 7 here is CoerceVisibility() method. As you 6 can see we do not allow changing this property 5 until fading animation is completed.

This 4 code is neither thread safe nor bug free. Its 3 only intention is to show the direction 2 :). So feel free to improve, edit and get 1 reputation ;).

Score: 22

You can't directly use the Visibility property 8 for a fade out because setting a trigger 7 on it will first Hide/Collapse the control, THEN 6 animate it. So basically you'll got an animation 5 on a collapsed control => nothing.

One 4 "reliable" way would be to introduce 3 a new Dependency Property (attached or not), say 2 IsOpen and setting a property trigger IsOpen=True on it with:

EnterAction:

  • Make sure Visibility is set to Visible
  • Fade in the Opacity from 0 to 1

ExitAction:

  • Visibility set to Visible at keyframe 0 and Collapsed/Hidden at the last Keyframe
  • Fade out the Opacity from 1 to 0.

Here's 1 an example:

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

I've been coming at this a slightly different 8 way - I've got an extended version of Ray's 7 answer to this question which adds a FadeIn() and FadeOut() extension 6 method to everything that collapses or shows 5 the element as appropriate, then instead 4 of making objects visible I can just call 3 FadeIn() and FadeOut() on them - and it 2 will work an any element without any specific 1 animation code.

    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);
    }
Score: 6

I realize this Question is a bit old, but 6 I have only read it now and I have tweaked 5 the code given by Anvaka. It supports binding 4 to Visibility (only when binding mode is 3 set to TwoWay). It also supports 2 different 2 duration values for FadeIn and FadeOut.

Here 1 is the 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));
    }    
  }
Score: 4

This is best done using a behavior

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

This is 7 specifically being applied to a border - I 6 haven't tried a user control but I expect 5 the same applies.

To use it, you need the 4 Blend Interactivity namespace:

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

And use this 3 markup on the Border that you want the behavior 2 on:

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

You'll need to add in the namespace for 1 the behavior class too..

Score: 2

Quite old now, but could you not just chain 1 the 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>
Score: 2

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

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

More Related questions