[WPF] Display an animated GIF image

Note: The code in this article is out of date; the current code is hosted on GitHub.

WPF is a great technology, but sometimes it seems to be missing some really basic features… A frequently mentioned example is the lack of support for animated GIF images. Actually, the GIF format itself is supported by the imaging API, but the Image control only shows the first frame of the animation.

Many solutions to this problem have been proposed on technical forums and blogs, usually variations of the following approaches:

  • Use the MediaElement control: unfortunately this control only supports URI like file:// or http://, not the pack:// URI schema used for WPF resources; this means the image can’t be included in the resources, it has to be in a separate file. Furthermore, transparency for GIF images isn’t supported in MediaElement, which makes the final result quite ugly
  • Use the PictureBox control from Windows Forms, via a WindowsFormsHost: I personnally dislike using WinForms controls in WPF, it really looks like a hack…
  • Create a custom control that inherits Image and handles the animation. Some solutions take advantage of the ImageAnimator class from System.Drawing (GDI), others use a WPF animation to change the current frame. It’s a rather “clean” approach, but it forces you to use a specific control for GIF images. Also, the solution using ImageAnimator turns out not to be very smooth, the animation is quite jerky.

As you might have guessed, I don’t find any of these solutions really satisfying… Furthermore, none of the implementations I’ve seen of the third approach handles the duration of each frame properly, they only assume that all frames last 100ms (which is almost always true, but almost isn’t good enough IMHO…). So I kept the best ideas from each approach I’ve seen, and I came up with my own solution. Here are the goals I set to attain:

  • No dependency on Windows Forms or GDI
  • Display the animated image in a standard Image control
  • Use the same XAML code for normal and animated images
  • Support for transparency
  • Correct handling of frame duration

To achieve this result, I started from a very simple, even obvious idea: to animate the image, all you have to do is apply an animation to the Source property of the Image control. WPF provides all the necessary tools to do that; in this case, the ObjectAnimationUsingKeyFrames class fits the bill perfectly: it allows to specify at what exact time a given value should be assigned to the property, which makes it easy to take the frame duration into account.

The next problem is to extract the frames from the image: fortunately WPF supports this natively, and the BitmapDecoder class provides a Frames property to do exactly that. So, no big difficulty so far…

Finally, last obstacle: extract the duration of each frame. It’s the part that took me the longest, because I needed to do some research… I first thought I would need to read the file manually and decode the binary data myself. But eventually the solution is quite simple, and takes advantage of the BitmapMetadata class. The only difficulty has been to find the “path” of the metadata that contains the delay, but after a few minutes of trial and error, here it is: /grctlext/Delay.

The final solution is implemented as an attached property named AnimatedSource, that applies to the Image control, and can be used instead of Source:

<Image Stretch="None" my:ImageBehavior.AnimatedSource="/Images/animation.gif" />

This property can also be assigned a normal (not animated) image, it will be displayed normally; therefore this property can be used without worrying about whether the image to display will be animated or not.

So in the end, all the goals have been achieved, and we even get some icing on the cake: this solution also works in the designer (at least in Visual Studio 2010), so the animation is immediately visible when you set the AnimatedSource property 🙂

Without further ado, here’s the complete code:

    public static class ImageBehavior
        #region AnimatedSource

        public static ImageSource GetAnimatedSource(Image obj)
            return (ImageSource)obj.GetValue(AnimatedSourceProperty);

        public static void SetAnimatedSource(Image obj, ImageSource value)
            obj.SetValue(AnimatedSourceProperty, value);

        public static readonly DependencyProperty AnimatedSourceProperty =
              new UIPropertyMetadata(

        private static void AnimatedSourceChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
            Image imageControl = o as Image;
            if (imageControl == null)

            var oldValue = e.OldValue as ImageSource;
            var newValue = e.NewValue as ImageSource;
            if (oldValue != null)
                imageControl.BeginAnimation(Image.SourceProperty, null);
            if (newValue != null)

        private static void InitAnimationOrImage(Image imageControl)
            BitmapSource source = GetAnimatedSource(imageControl) as BitmapSource;
            if (source != null)
                var decoder = GetDecoder(source) as GifBitmapDecoder;
                if (decoder != null && decoder.Frames.Count > 1)
                    var animation = new ObjectAnimationUsingKeyFrames();
                    var totalDuration = TimeSpan.Zero;
                    BitmapSource prevFrame = null;
                    FrameInfo prevInfo = null;
                    foreach (var rawFrame in decoder.Frames)
                        var info = GetFrameInfo(rawFrame);
                        var frame = MakeFrame(
                            rawFrame, info,
                            prevFrame, prevInfo);

                        var keyFrame = new DiscreteObjectKeyFrame(frame, totalDuration);
                        totalDuration += info.Delay;
                        prevFrame = frame;
                        prevInfo = info;
                    animation.Duration = totalDuration;
                    animation.RepeatBehavior = RepeatBehavior.Forever;
                    if (animation.KeyFrames.Count > 0)
                        imageControl.Source = (ImageSource)animation.KeyFrames[0].Value;
                        imageControl.Source = decoder.Frames[0];
                    imageControl.BeginAnimation(Image.SourceProperty, animation);
            imageControl.Source = source;

        private static BitmapDecoder GetDecoder(BitmapSource image)
            BitmapDecoder decoder = null;
            var frame = image as BitmapFrame;
            if (frame != null)
                decoder = frame.Decoder;

            if (decoder == null)
                var bmp = image as BitmapImage;
                if (bmp != null)
                    if (bmp.StreamSource != null)
                        bmp.StreamSource.Position = 0;
                        decoder = BitmapDecoder.Create(bmp.StreamSource, bmp.CreateOptions, bmp.CacheOption);
                    else if (bmp.UriSource != null)
                        Uri uri = bmp.UriSource;
                        if (bmp.BaseUri != null && !uri.IsAbsoluteUri)
                            uri = new Uri(bmp.BaseUri, uri);
                        decoder = BitmapDecoder.Create(uri, bmp.CreateOptions, bmp.CacheOption);

            return decoder;

        private static BitmapSource MakeFrame(
            BitmapSource fullImage,
            BitmapSource rawFrame, FrameInfo frameInfo,
            BitmapSource previousFrame, FrameInfo previousFrameInfo)
            DrawingVisual visual = new DrawingVisual();
            using (var context = visual.RenderOpen())
                if (previousFrameInfo != null && previousFrame != null &&
                    previousFrameInfo.DisposalMethod == FrameDisposalMethod.Combine)
                    var fullRect = new Rect(0, 0, fullImage.PixelWidth, fullImage.PixelHeight);
                    context.DrawImage(previousFrame, fullRect);

                context.DrawImage(rawFrame, frameInfo.Rect);
            var bitmap = new RenderTargetBitmap(
                fullImage.PixelWidth, fullImage.PixelHeight,
                fullImage.DpiX, fullImage.DpiY,
            return bitmap;

        private class FrameInfo
            public TimeSpan Delay { get; set; }
            public FrameDisposalMethod DisposalMethod { get; set; }
            public double Width { get; set; }
            public double Height { get; set; }
            public double Left { get; set; }
            public double Top { get; set; }

            public Rect Rect
                get { return new Rect(Left, Top, Width, Height); }

        private enum FrameDisposalMethod
            Replace = 0,
            Combine = 1,
            RestoreBackground = 2,
            RestorePrevious = 3

        private static FrameInfo GetFrameInfo(BitmapFrame frame)
            var frameInfo = new FrameInfo
                Delay = TimeSpan.FromMilliseconds(100),
                DisposalMethod = FrameDisposalMethod.Replace,
                Width = frame.PixelWidth,
                Height = frame.PixelHeight,
                Left = 0,
                Top = 0

            BitmapMetadata metadata;
                metadata = frame.Metadata as BitmapMetadata;
                if (metadata != null)
                    const string delayQuery = "/grctlext/Delay";
                    const string disposalQuery = "/grctlext/Disposal";
                    const string widthQuery = "/imgdesc/Width";
                    const string heightQuery = "/imgdesc/Height";
                    const string leftQuery = "/imgdesc/Left";
                    const string topQuery = "/imgdesc/Top";

                    var delay = metadata.GetQueryOrNull<ushort>(delayQuery);
                    if (delay.HasValue)
                        frameInfo.Delay = TimeSpan.FromMilliseconds(10 * delay.Value);

                    var disposal = metadata.GetQueryOrNull<byte>(disposalQuery);
                    if (disposal.HasValue)
                        frameInfo.DisposalMethod = (FrameDisposalMethod) disposal.Value;

                    var width = metadata.GetQueryOrNull<ushort>(widthQuery);
                    if (width.HasValue)
                        frameInfo.Width = width.Value;

                    var height = metadata.GetQueryOrNull<ushort>(heightQuery);
                    if (height.HasValue)
                        frameInfo.Height = height.Value;

                    var left = metadata.GetQueryOrNull<ushort>(leftQuery);
                    if (left.HasValue)
                        frameInfo.Left = left.Value;

                    var top = metadata.GetQueryOrNull<ushort>(topQuery);
                    if (top.HasValue)
                        frameInfo.Top = top.Value;
            catch (NotSupportedException)

            return frameInfo;

        private static T? GetQueryOrNull<T>(this BitmapMetadata metadata, string query)
            where T : struct
            if (metadata.ContainsQuery(query))
                object value = metadata.GetQuery(query);
                if (value != null)
                    return (T) value;
            return null;


And here’s the DoWhenLoaded extension method used in the code above:

public static void DoWhenLoaded<T>(this T element, Action<T> action)
    where T : FrameworkElement
    if (element.IsLoaded)
        RoutedEventHandler handler = null;
        handler = (sender, e) =>
            element.Loaded -= handler;
        element.Loaded += handler;

Enjoy 🙂

Update: the code that retrieves the frame duration only works on Windows Seven, and on Windows Vista if the Platform Update is installed (untested). The default duration (100ms) will be used instead on other versions of Windows. I will update the article if I find a solution that works on all operating systems (I know I could use System.Drawing.Bitmap, but I’d rather not depend on this…)

Update 2: as pointed out by Klaus in the comments, the ImageBehavior class didn’t handle some important attributes of the frames: the diposal method (whether a frame should entirely replace the previous one, or be combined with it), and the frame position (Left/Top/Width/Height). I updated the code to handle these attributes properly. Thank you Klaus!

Update 3: a commenter on the French version of my blog pointed out a problem when the AnimatedSource is an image in a resource dictionary; the UriSource wasn’t correctly interpreted when it was a relative URI. This problem is now fixed. Thank you, “anonymous”!

Update 4: uploaded an example project to demonstrate the code.

Update 5: yet another bug fix, for when you use a BitmapImage initialized from a stream. Thanks to Mizutama for spotting this one!

Update 6: rather than posting improvements to this blog post, I eventually created a project on CodePlex GitHub where this class will be maintained. You can also install it using NuGet, the package id is WpfAnimatedGif. Thanks to Diego Mijelshon for the suggestion!

kick it on DotNetKicks.com

63 thoughts on “[WPF] Display an animated GIF image”

    1. Hi Marc,
      For me the animation works smoothly… Perhaps there is something unusual in the gif you”re trying to display. Could you post it somewhere so I can look into the problem?

      1. It gets jump if you are doing something on the UI thread that blocks the dispatcher from updating the animation. Refactor those calls into BackgroundWorker.DoWork/Completed structure and it runs smoothly.

  1. Hi Thomas,
    It looks good but in wpf app it gives me an error on foll. line

    that : the attachable property “AnimatedSource” was not found in type “ImageBehavior”.
    can u please help me resolve this problem?
    Thannks in advance.

    1. Hi Vitthal,
      This error can appear in the designer, but it should disappear once you compile the project. Make sure your namespace declarations are correct.

    1. Vitthal,

      You need an attribute like that on the root element:


      where MyNamespace is the C# namespace where you put the ImageBehavior class.

      If the class is in another assembly, use this syntax:


      See this page for details on XAML namespace mapping

      1. Thomas thanks again for the reply.
        As i”m new to WPF i don”t know what to write in the class ImageBehavior n where should i create it.
        So can u please send me the details of class.

        1. Vitthal,
          The source of the ImageBehavior class is in the blog post… just click “show source” to expand it. You can copy it and paste it anywhere in your project.

  2. Hi Thomas.
    I don”t know where i”m wrong.
    On the xaml.vb file i have added foll. code
    Namespace ImageBehave
    End Namespace

    And in this namspace i added the class provided by you.
    Then on xaml page i reffered it like..

    But it shows me the same error.
    I done it using vb.net.

    One more error for extension method
    that it can write in module.

    I really stuck here.
    Please help me to get out of this.

    Thank you.

  3. Thanks… thought this was great!
    Question on something I was going to look into. Do you think it would be possible to pause the animation – say – on a trigger/datatrigger? I guess switch the animated source to something that is not animated would be the simple/quick way?

    1. Hi Jimmy,

      Sure, you can do that. AnimatedSource is an attached property, so you can set it in a trigger. I think the easiest way to stop the animation would be to set AnimatedSource to null, and assign the GIF image to Source instead. Note that it wouldn”t exactly pause the animation on its current frame, it would display the first frame… You could probably modify the code so that it really suspends the animation, but I”m not sure how to do it.

  4. Very well done. Thank you. For what it”s worth, because my project messes around with the StreamSource, I needed to do a Seek back to the start before creating the decoder in ImageBehavior.GetDecoder().

    Also, thanks for using the “as” operator correctly. I so tire of examples that use “as” and then don”t check for null.

  5. This is very clever, thank you!

    Many animated GIFs do not use ”replace” mode, but ”combine” mode, i.e. the next frame shall be painted on top of what is already there. This can be recognized by checking the “/grctlext/Disposal” metadata (1 for combine mode).
    In this case, the DiscreteObjectKeyFrames cannot just contain the original frames from the GifBitmapDecoder, but one has to combine the previous frame with the new one, e.g. by using RenderTargetBitmap with two stacked Image controls. It is also important to take “/imgdesc/Left” and “/imgdesc/Top” into account.


    1. Hi Klaus,

      Thanks for your comment, I wasn”t aware of these subtleties of the GIF format! I would gladly improve the code to handle them, but I don”t have any images with the characteristics you mention ; do you know where I could find some examples?


  6. Hi Thomas,

    very nice piece of code, excellent work! No wonder your post shows up as the first result for “XAML Animated GIF” 🙂 One quick question though: How can I make a regular Image Control to be animated from code? Is that possible? Would help a lot, thank you in advance!

    P.S. : I”ve also tried to change the visibility of the Image whose animation I attached using your code, to no avail. Just a swift observation from my part. Keep up the good work, cheers!

    1. Hi,

      You can set the AnimatedSource property from code :
      ImageBehavior.SetAnimatedSource(imageControl, imageSource);

      As for the Visibility, I”m not sure what the problem is… my code isn”t doing anything with this property.


  7. Hi Thomas,

    Thanks for the code. However, I”m having memory issues with it – since I started using it, my application consumes hundreds of MB (while before it needed only 50 MB)….


    1. Some more info – I”m having 124 frames in the image. Here is a typical frame info –

      delay = 0.03 sec
      DisposalMethod = Combine
      Height = 760
      Width = 650

      1. Hi Guyguy,

        Actually it”s not really surprising… You”re not loading 1 image, but rather 124 images, so it”s bound to use up a lot of memory. If you display your GIF in another app (a web browser for instance) you will probably notice a similar increase in memory usage. Large animated GIFs with so many frames are quite uncommon, mainly for this reason. Perhaps you should consider using a video instead.


  8. Thanks for the answer. I actually thought about using a video instead, only that I need the transparency in the image…
    Do you think it could be possible to load the frames in chunks and not all together, so that there is no need to hold all frames in memory at the same time? Or maybe a better solution?

    1. Mmm… that”s a hard one. I guess it would be possible to load the frames on demand and avoid keeping them in memory, but since they”re only 0.03 second apart from each other, that would hurt performance a lot…

  9. Hello.
    I have tonz of errors tring make this work

    Plese can upload compete sourcecode example, working so I cnan donload test and see why what im doing is not working?
    Please!!! : )

    1. Hi Carlos,
      I”m on holidays so I can”t post an example now. The code should contain everything you need, I only omitted is the using directives because Visual Studio can add them automatically (using the smart tags).

  10. Hello,

    I wonder if it is possible to delete an GIF this way?
    I mean that at first i just allowed jpg”s, but now i want to allow GIF”s, this worked alright. But my application allows to delete the file from disk, and you can see where i am going at, when I delete, it of course said “File being used by another process”.

    When using images this was easy:
    BitmapImage image = new BitmapImage();
    image.CacheOption = BitmapCacheOption.OnLoad;
    image.UriSource = new Uri(path);

    But here, i can”t find the right solution for this, Can you think of a way to achieve this?

    1. Nevermind,

      That code i posted was wrapped in a converter, and when I apply this converter on your dependency property, it works. I forgot you mentioned your dependency property reacts just like the original Source property of

      So everything just works perfectly 🙂 Thx!

    1. Are you using Windows XP? Unfortunately on XP the metadata for the frames isn”t available, so I don”t have the necessary information about where and when to display the frames… You could try to reencode the images differently so that each frame contains the whole image (rather than just the diff between consecutive images)

    1. Hi,
      The problem seems to be that in your images the delay has a value of 0… In this case you should probably use the default value. Just change the code a bit in the GetFrameInfo method: replace if (delay.HasValue) with if (delay.HasValue && delay.Value > 0)

  11. I”ve been using this code a bit and really, really want it to work for me. I got it to work using a new property for some cases where I have to bind directly to a Bitmap instance:

    controls:ImageBehavior.BitmapContent=”{Binding AdImage}”

    I do this by converting the System.Drawing.Bitmap to a System.Windows.Media.Imaging.BitmapImage and then using that through the rest of your code.

    Now, my only problem is that when I do so, the images become blurry. Text that was crips now seems blurry. I think that this is due to the use of the RenderTargetBitmap, which only supports PixelFormats.Pbgra32, but I”m not 100% sure. Some blurring or interpolation seems to happen in there.

    Have you or anyone else noticed this? You can try this image for an example:

    1. Hi Greg,
      Why do you have to use a System.Drawing.Bitmap? Can”t you use a WPF image directly? Anyway, I don”t think your problem is related to my code, but rather to the code that converts the image…

      1. I had to use a System.Drawing.Bitmap because someone else”s code was providing that type. Originally, it was using a custom control to bind to an instance of a Bitmap. Basically, there”s a web service that”s providing an image and the client code loads it as a Bitmap. Maybe I could have changed that, but probably would have still had to make some changes, since I have an object, nor a URI or a resource. I didn”t have time to change the code that was providing the Bitmap.

    2. Ah, never mind! It had more to do with either UseLayoutRounding or SnapsToDevicePixels on some parent elements, I think. Looks good now … and thanks for the great code!

  12. GetDecoder( image ) throw exception if image is not GIF.
    For example following BitmapImage could not used for AnimatedSource;

        FileStream stream = new FileStream( &quot;test.jpg&quot; , FileMode.Open , FileAccess.Read );
        BitmapImage bi = new BitmapImage();
        bi.StreamSource = stream;

    Now I found solution;

        if (bmp.StreamSource != null)
            bmp.StreamSource.Seek( 0 , System.IO.SeekOrigin.Begin );
            decoder = BitmapDecoder.Create(bmp.StreamSource, bmp.CreateOptions, bmp.CacheOption);
      1. One more thing, it”s not important, for following circumstance;
        var wc = new System.Net.WebClient();
        //var stream = wc.OpenRead( “http://www.nonstopgifs.com/animated-gifs/games/games-animated-gif-003.gif” );
        var stream = wc.OpenRead( “http://www.jpeg.org/images/sky_main.jpg” );
        BitmapImage bi = new BitmapImage();
        bi.StreamSource = stream;

        ImageBehavior.SetAnimatedSource( BehaviorAniGIF , bi );

        096 if (bmp.StreamSource != null)
        097 {
        if ( bmp.StreamSource.CanSeek )
        098 bmp.StreamSource.Position = 0;
        099 decoder = BitmapDecoder.Create(bmp.StreamSource, bmp.CreateOptions, bmp.CacheOption);
        100 }

        Notice that http://www.nonstopgifs.com/animated-gifs/games/games-animated-gif-003.gif would not be Animated.
        It can be the limitaion of this behavior because you could use downloaded data (raw byte array or MemoryStream or something)
        instead of using stream returned from wc.OpenRead() directly.

  13. hi! thanx for your project. it works nice… but only in windows 7(((. have resolved this problem somehow?
    And another problem is that my gif images are cutted. haven”t find the reason yet. can it depend on gifs ?

    1. Hi,
      I have a solution that works on Windows XP, but its a little ugly, which is why I didn”t mention it in this blog post… Basically, it requires manual decoding of the GIF frame metadata. You can get the code here, but it comes with no guarantees…

  14. Thanks for your work. Just one small improvement to handle some GIF files that has Delay set to 0 in metadata, change the following line in method GetFrameInfo from

    if (delay.HasValue)


    if (delay.HasValue && delay.Value > 0)

  15. After a big search on the web, this was absolutely the best solution! I was stuck with a WebBrowser component, and this really helped me!
    Thank you Thomas, keep up the good work
    Cheers from Brazil

  16. I get the following error:

    ‘Provide value on ‘System.Windows.Baml2006.TypeConverterMarkupExtension’ threw an exception.’

    1. Hi Denis,
      Are you using the project from CodePlex or the Nuget package? Don’t use the code from this post, it’s not up to date.

    2. Make sure to include the gif file in your project throw “Solution Explorer”

      Go to the property of the gif file and make sure the Build Action is “Resource”

      Another trick is to assign the gif directly to the Source property in the Properties window for the Image control and then change “Source” to “gif:ImageBehavior.AnimatedSource” in xaml.

  17. Hi,
    I’m using the control on 1st Tab of the window but when I move to any other tab and comes back to 1st Tab the animation gets stopped.
    How can I restart the animation when the 1st Tab is selected again.

    I tried to get the controller handle on Tab shift and try to call the controller.Play() but without success.
    Any Idea how can I handle this?
    Thanks in advance

  18. Hi Thomas,
    Thanks for the prompt response. I have attached the sample code in the CodePlex side and wonder if you could please have a look and any feedback would be appreciated.

Leave a Reply

Your email address will not be published. Required fields are marked *