Back in this post I showed you how you can easily add a fade-in / fade-out effect to a UIElement that changes its Visibility property, using a simple attached property.
Some people encountered a problem using this property when they bind the UIElement to a model which initially hides the control. Since the default value of the Visibility property is Visible, using the attached property created an unwanted fade-out animation when the application started.
To fix this issue I added another attached property that allows the user to skip the first animation.
Also, I’ve fixed a minor issue with the double animation of the opacity.
For completion, I bring here the full updated source. For more details on how the animation works, check the original post.
That’s it for now, Arik Poznanski.
Appendix A – Updated Source Code for VisibilityAnimation class
using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Data;
using System.Windows.Media.Animation;
namespace WPF.Common
{
public class VisibilityAnimation
{
public enum AnimationType
{
None,
Fade
}
private const int ANIMATION_DURATION = 200;
private static readonly Dictionary<FrameworkElement, bool> _hookedElements =
new Dictionary<FrameworkElement, bool>();
public static AnimationType GetAnimationType(DependencyObject obj)
{
return (AnimationType)obj.GetValue(AnimationTypeProperty);
}
public static void SetAnimationType(DependencyObject obj, AnimationType value)
{
obj.SetValue(AnimationTypeProperty, value);
}
public static readonly DependencyProperty AnimationTypeProperty =
DependencyProperty.RegisterAttached(
"AnimationType",
typeof(AnimationType),
typeof(VisibilityAnimation),
new FrameworkPropertyMetadata(AnimationType.None,
new PropertyChangedCallback(OnAnimationTypePropertyChanged)));
public static bool GetIgnoreFirstTime(DependencyObject obj)
{
return (bool)obj.GetValue(IgnoreFirstTimeProperty);
}
public static void SetIgnoreFirstTime(DependencyObject obj, bool value)
{
obj.SetValue(IgnoreFirstTimeProperty, value);
}
public static readonly DependencyProperty IgnoreFirstTimeProperty =
DependencyProperty.RegisterAttached(
"IgnoreFirstTime",
typeof(bool),
typeof(VisibilityAnimation),
new UIPropertyMetadata(false));
private static void OnAnimationTypePropertyChanged(
DependencyObject dependencyObject,
DependencyPropertyChangedEventArgs e)
{
var frameworkElement = dependencyObject as FrameworkElement;
if (frameworkElement == null)
{
return;
}
if (GetAnimationType(frameworkElement) != AnimationType.None)
{
HookVisibilityChanges(frameworkElement);
}
else
{
UnHookVisibilityChanges(frameworkElement);
}
}
private static void HookVisibilityChanges(FrameworkElement frameworkElement)
{
_hookedElements.Add(frameworkElement, false);
}
private static void UnHookVisibilityChanges(FrameworkElement frameworkElement)
{
if (_hookedElements.ContainsKey(frameworkElement))
{
_hookedElements.Remove(frameworkElement);
}
}
static VisibilityAnimation()
{
UIElement.VisibilityProperty.AddOwner(
typeof(FrameworkElement),
new FrameworkPropertyMetadata(
Visibility.Visible,
VisibilityChanged,
CoerceVisibility));
}
private static void VisibilityChanged(
DependencyObject dependencyObject,
DependencyPropertyChangedEventArgs e)
{
}
private static object CoerceVisibility(
DependencyObject dependencyObject,
object baseValue)
{
var frameworkElement = dependencyObject as FrameworkElement;
if (frameworkElement == null)
{
return baseValue;
}
var visibility = (Visibility)baseValue;
if (visibility == frameworkElement.Visibility)
{
return baseValue;
}
if (!IsHookedElement(frameworkElement))
{
return baseValue;
}
if (GetIgnoreFirstTime(frameworkElement))
{
SetIgnoreFirstTime(frameworkElement, false);
return baseValue;
}
if (UpdateAnimationStartedFlag(frameworkElement))
{
return baseValue;
}
var doubleAnimation = new DoubleAnimation
{
Duration = new Duration(TimeSpan.FromMilliseconds(ANIMATION_DURATION))
};
doubleAnimation.Completed += (sender, eventArgs) =>
{
if (visibility == Visibility.Visible)
{
UpdateAnimationStartedFlag(frameworkElement);
}
else
{
if (BindingOperations.IsDataBound(frameworkElement,
UIElement.VisibilityProperty))
{
Binding bindingValue = BindingOperations.GetBinding(frameworkElement,
UIElement.VisibilityProperty);
BindingOperations.SetBinding(frameworkElement,
UIElement.VisibilityProperty, bindingValue);
}
else
{
frameworkElement.Visibility = visibility;
}
}
};
if (visibility == Visibility.Collapsed || visibility == Visibility.Hidden)
{
doubleAnimation.From = (double)frameworkElement.GetValue(
UIElement.OpacityProperty);
doubleAnimation.To = 0.0;
}
else
{
doubleAnimation.From = (double)frameworkElement.GetValue(
UIElement.OpacityProperty);
doubleAnimation.To = 1.0;
}
frameworkElement.BeginAnimation(UIElement.OpacityProperty, doubleAnimation);
return Visibility.Visible;
}
private static bool IsHookedElement(FrameworkElement frameworkElement)
{
return _hookedElements.ContainsKey(frameworkElement);
}
private static bool UpdateAnimationStartedFlag(FrameworkElement frameworkElement)
{
var animationStarted = _hookedElements[frameworkElement];
_hookedElements[frameworkElement] = !animationStarted;
return animationStarted;
}
}
}
Arik Poznanski is a senior software developer at Verint. He completed two B.Sc. degrees in Mathematics & Computer Science, summa cum laude, from the Technion in Israel.
Arik has extensive knowledge and experience in many Microsoft technologies, including .NET with C#, WPF, Silverlight, WinForms, Interop, COM/ATL programming, C++ Win32 programming and reverse engineering (assembly, IL).