Elad’s WPF Blog

April 2, 2009

Animated GIF Support Behavior

Filed under: Programming, WPF — Tags: , , , — Elad Malki @ 4:50 pm

After talking about Attached Behavior in the last post, this is an example of a behavior which adds an animated GIF support to regular WPF Image control.

WPF does not support naturally displaying an animated GIF. There are couple of ways to overcome this problem, and using this behavior is one of them. Basically, the behavior creates an animation that loops over the animated gif frames, setting them as a source for the image.

you can download the source and sample here

The usage example is:

<Window x:Class="AnimatedGifBehaviorTest.Window2"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:AnimatedGifBehaviorTest"
        
    Title="Window2" Height="300" Width="300">
    <Grid>
            <Image local:SupportAnimatedGIFBehviour.SupportAnimatedGif="True"
                   Source="MyImage.gif" Width="32" Height="32"/>
    </Grid>
</Window>

And the behavior implementation is:
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Media.Imaging;

namespace AnimatedGifBehaviorTest
{
    public static class SupportAnimatedGIFBehviour
    {
        private static readonly int MILLISCONDS_PER_FRAME = 75;

        #region SupportAnimatedGif Attached Property

        [AttachedPropertyBrowsableForType(typeof(Image))]
        public static bool GetSupportAnimatedGif(Image image)
        {
            return (bool)image.GetValue(SupportAnimatedGifProperty);
        }

        public static void SetSupportAnimatedGif(Image image, bool value)
        {
            image.SetValue(SupportAnimatedGifProperty, value);
        }

        /// <summary>
        /// An Attached Property for Animated GIF support.
        /// </summary>
        public static readonly DependencyProperty SupportAnimatedGifProperty =
            DependencyProperty.RegisterAttached(
            "SupportAnimatedGif",
            typeof(bool),
            typeof(SupportAnimatedGIFBehviour),
            new UIPropertyMetadata(false, OnSupportAnimatedGifChanged));

        /// <summary>
        /// Register \ UnRegister to visibility changes and image source changes.
        /// </summary>
        /// <param name="depObj">The image object.</param>
        /// <param name="e">The SupportAnimatedGif Property values.</param>
        private static void OnSupportAnimatedGifChanged(DependencyObject depObj, DependencyPropertyChangedEventArgs e)
        {
            bool isSupportingAnimatedGIF = (bool)e.NewValue;
            Image image = (Image)depObj;

            if (isSupportingAnimatedGIF)
            {
                RegisterForRelevantImagePropertyChanges(image);
                
                if (image.Visibility == Visibility.Visible)
                {
                    image.StartFramesAnimation();
                }
            }
            else
            {
                UnRegisterForRelevantImagePropertyChanges(image);
                StopFramesAnimation(image);
            }
        }

        private static DependencyPropertyDescriptor VisibilityDPDescriptor
        {
            get
            {
                return DependencyPropertyDescriptor.FromProperty(UIElement.VisibilityProperty, typeof(UIElement));
            }
        }

        private static DependencyPropertyDescriptor ImageSourceDPDescriptor
        {
            get
            {
                return DependencyPropertyDescriptor.FromProperty(Image.SourceProperty, typeof(Image));
            }
        }

        private static void UnRegisterForRelevantImagePropertyChanges(DependencyObject depObj)
        {
            VisibilityDPDescriptor.RemoveValueChanged(depObj, OnVisibilityChanged);
            ImageSourceDPDescriptor.RemoveValueChanged(depObj, OnImageSourceChanged);            
        }

        private static void RegisterForRelevantImagePropertyChanges(DependencyObject depObj)
        {
            VisibilityDPDescriptor.AddValueChanged(depObj, OnVisibilityChanged);
            ImageSourceDPDescriptor.AddValueChanged(depObj, OnImageSourceChanged);
        }

        #endregion

        #region CurrentFrameIndex Dependency Property

        private static readonly DependencyProperty CurrentFrameIndexProperty = DependencyProperty.Register(
            "CurrentFrameIndex",
            typeof(int),
            typeof(Image),
            new PropertyMetadata(0, OnCurrentFrameIndexChanged));

        #endregion

        #region IsAnimationChangingFrame Dependency Property

        /// <summary>
        /// IsAnimationChangingFrame Dependency Property
        /// </summary>
        private static readonly DependencyProperty IsAnimationChangingFrameProperty = DependencyProperty.Register(
            "IsAnimationChangingFrame",
            typeof(bool),
            typeof(Image),
            new PropertyMetadata(false));

        /// <summary>
        /// DummyImage Dependency Property - for keeping the original source binding when animation is applied
        /// </summary>
        private static readonly DependencyProperty DummyImageProperty = DependencyProperty.Register(
            "DummyImage",
            typeof(ImageSource),
            typeof(Image),
            new PropertyMetadata(null, OnDummyImagePropertyChanged));

        /// <summary>
        /// Gets or sets the IsAnimationChangingFrame property. This dependency property 
        /// indicates that the animation is currently changing the image source.
        /// </summary>
        private static bool GetIsAnimationChangingFrame(this Image image)
        {
            return (bool)image.GetValue(IsAnimationChangingFrameProperty);
        }
        private static void SetIsAnimationChangingFrame(this Image image, bool isAnimationChangingFrame)
        {
            image.SetValue(IsAnimationChangingFrameProperty, isAnimationChangingFrame);
        }

        #endregion

        private static void OnImageSourceChanged(object sender, EventArgs e)
        {
            Image image = (Image)sender;

            if (!image.GetIsAnimationChangingFrame())//If the image source is changing by an outer source(not the animation).
            {
                //stop old animation
                image.SetIsAnimationChangingFrame(true);
                image.StopFramesAnimation();
                image.SetIsAnimationChangingFrame(false);

                //start new animation - only if frames count is bigger than one.
                image.StartFramesAnimation();
            }
        }

        /// <summary>
        /// Starts frame animation only if frames count is bigger than 1.
        /// </summary>
        /// <param name="image"></param>
        private static void StartFramesAnimation(this Image image)
        {
            BitmapFrame bitmapFrame = image.Source as BitmapFrame;
            if (bitmapFrame != null)
            {
                int framesCount = bitmapFrame.Decoder.Frames.Count;

                if (framesCount > 1)
                {
                    Int32Animation gifAnimation =
                        new Int32Animation(
                            0, // "From" value
                            framesCount - 1, // "To" value
                            new Duration(TimeSpan.FromMilliseconds(MILLISCONDS_PER_FRAME * framesCount))
                        );

                    gifAnimation.RepeatBehavior = RepeatBehavior.Forever;

                    image.BeginAnimation(CurrentFrameIndexProperty, gifAnimation, HandoffBehavior.SnapshotAndReplace);
                }
            }
        }

        private static void StopFramesAnimation(this Image image)
        {
            image.BeginAnimation(CurrentFrameIndexProperty, null);
        }

        private static void OnVisibilityChanged(object sender, EventArgs e)
        {
            Image image = (Image)sender;

            if (image.Visibility != Visibility.Visible)
            {
                image.StopFramesAnimation();
            }
            else
            {
                image.StartFramesAnimation();
            }
        }

        private static void OnDummyImagePropertyChanged(DependencyObject dpo, DependencyPropertyChangedEventArgs e)
        {
            Image animatedImage = (Image)dpo;

            if (!animatedImage.GetIsAnimationChangingFrame())
            {
                BindingBase originalBinding = BindingOperations.GetBindingBase(dpo, DummyImageProperty);
                if (originalBinding != null)
                {
                    BindingOperations.SetBinding(dpo, Image.SourceProperty, originalBinding);
                    BindingOperations.ClearBinding(animatedImage, DummyImageProperty);
                }
                animatedImage.SetIsAnimationChangingFrame(false);
            }
            else
            {
                animatedImage.SetIsAnimationChangingFrame(false);
            }

            animatedImage.SetIsAnimationChangingFrame(false);
        }

        /// <summary>
        /// Update the current image source to the relevenat frame.
        /// </summary>
        /// <param name="dpo"></param>
        /// <param name="e"></param>
        private static void OnCurrentFrameIndexChanged(DependencyObject dpo, DependencyPropertyChangedEventArgs e)
        {
            Image animatedImage = (Image)dpo;

            if (!animatedImage.GetIsAnimationChangingFrame())
            {
                animatedImage.SetIsAnimationChangingFrame(true);

                bool hasBinding = BindingOperations.IsDataBound(animatedImage, Image.SourceProperty);
                if (hasBinding)
                {
                    BindingBase originalBinding = BindingOperations.GetBindingBase(animatedImage, Image.SourceProperty);
                    BindingOperations.SetBinding(animatedImage, DummyImageProperty, originalBinding);
                }
                animatedImage.Source = ((BitmapFrame)animatedImage.Source).Decoder.Frames[(int)e.NewValue];

                if (!hasBinding)
                {
                    animatedImage.SetIsAnimationChangingFrame(false);
                }
            }
        }
    }
}

About these ads

3 Comments »

  1. Great post – excellent example.
    Thanks! saved me lots of time

    Comment by orgar — July 21, 2009 @ 12:39 pm

  2. Hey great work. I was very excited to find it.

    I refer to your work in this wic msdn forum:
    http://social.msdn.microsoft.com/Forums/en-US/windowswic/thread/5014fd2d-1721-4ea7-a4a6-9940b142d935

    Let me know if you have any suggestions! :)

    Thanks again, great behavior.

    –dave

    Comment by David Horner — January 23, 2010 @ 12:44 am

  3. Hello Elad,
    Nice Article. Excellent.

    I need help to convert XAML TO C# in the above code. It is working fine in the design time. but i want to assign the values in the runtime like SupportAnimatedGIFBehaviour.SupportAnimatedGif in Image object. How do i do that
    Please help me

    Thanks & Advance
    Britto

    Comment by Britto — July 24, 2010 @ 6:36 pm


RSS feed for comments on this post. TrackBack URI

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

The Shocking Blue Green Theme Blog at WordPress.com.

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: