Search code examples
c#winforms

How to create a control in windows forms that will display and play animated gif?


I tried this code but it's showing the last frame. and if i set the frame delay to 1000 then it's waiting one second before showing anything and then show the last frame. it's not playing all the frames from first to last.

I tried to load the animated gif first in a pictureBox but the same behavior.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Imaging;
using System.Windows.Forms;

public class AnimatedGifControl : Control
{
    private List<Image> frames;
    private int currentFrameIndex;
    private Timer animationTimer;

    [Browsable(true)]
    [Category("Behavior")]
    [Description("The delay between frames in milliseconds.")]
    public int FrameDelay { get; set; } = 100; // Default frame delay

    public AnimatedGifControl()
    {
        frames = new List<Image>();
        DoubleBuffered = true;
        animationTimer = new Timer();
        animationTimer.Tick += OnAnimationTick;
    }

    public void LoadGif(string gifPath)
    {
        frames.Clear();
        using (Image gifImage = Image.FromFile(gifPath))
        {
            int frameCount = gifImage.GetFrameCount(FrameDimension.Time);
            for (int i = 0; i < frameCount; i++)
            {
                gifImage.SelectActiveFrame(FrameDimension.Time, i);
                frames.Add(new Bitmap(gifImage));
            }
        }

        currentFrameIndex = 0;
        if (frames.Count > 0)
        {
            animationTimer.Interval = FrameDelay;
            animationTimer.Start();
        }
    }

    private void OnAnimationTick(object sender, EventArgs e)
    {
        currentFrameIndex = (currentFrameIndex + 1) % frames.Count;
        Invalidate();
    }

    protected override void OnPaint(PaintEventArgs e)
    {
        base.OnPaint(e);
        if (frames.Count > 0)
        {
            e.Graphics.DrawImage(frames[currentFrameIndex], 0, 0, Width, Height);
        }
    }

    protected override void Dispose(bool disposing)
    {
        if (disposing)
        {
            animationTimer.Dispose();
            foreach (var frame in frames)
            {
                frame.Dispose();
            }
        }
        base.Dispose(disposing);
    }
}

Solution

  • OK I found the issue. In Design-Time you have the FrameDelay set:

    animatedGifControl1.FrameDelay = 100;
    

    For some reason that prevents setting the Interval here during Run-Time:

    public int FrameDelay { get; set; } = 120000;
    

    ...

    if (frames.Count > 0)
    {
        animationTimer.Interval = FrameDelay;
        animationTimer.Start();
    

    To solve the issue remove the Design-Time code that sets FrameDelay in the InitializeComponent method.

    The way I worked this out was putting this in the Tick event:

    Debug.WriteLine("Tick:" + DateTime.Now.ToString("o") + "-" + animationTimer.Interval);