Search code examples
vb.netwinformsgraphicspictureboxanimated-gif

How to play a GIF animation to the last Frame, then stop the animation?


In my project, I want to play a GIF in a PictureBox.
I need to play all Frames the GIF animation contains, then stop the animation.

I'm using the ImageAnimator class to animate a GIF Image, I just don't know how to stop it.

Private image As Image = My.Resources.icon_confirmation
'Private frames As Integer
Dim FDimensions As System.Drawing.Imaging.FrameDimension = New System.Drawing.Imaging.FrameDimension(image.FrameDimensionsList(0))
Dim frames As Integer = image.GetFrameCount(FDimensions)

Private Sub paintFrame(ByVal sender As Object, ByVal e As EventArgs)
    If frames < 33 Then PictureBox1.Image = image Else ImageAnimator.StopAnimate(image, AddressOf StopAnim)
End Sub

Private Sub StopAnim(ByVal sender As Object, ByVal e As EventArgs)
    PictureBox1.Dispose()
End Sub

Private Sub PictureBox1_Paint(ByVal sender As Object, ByVal e As System.Windows.Forms.PaintEventArgs) Handles PictureBox1.Paint
    If frames = 12 Then
        ImageAnimator.UpdateFrames()
        e.Graphics.DrawImage(image, Point.Empty)
        frames -= 1
    End If
End Sub

GIF


Solution

  • To keep track of the current Frame that's being drawn on your PictureBox, you need a Field to store the current progress and compare it with the number of Frames that the animation contains.

    When the progress reaches the last Frame (or any other Frame before the last, whatever is needed), you stop the animation calling ImageAnimator.StopAnimate().

    To start the animation, you first check whether ImageAnimator.CanAnimate() (it may not be able to animate the Image you specified). If it can, then you call ImageAnimator.Animate(), passing to the method the Image object and the address of the method that handles the FrameChanged event.

    This handler is used to check whether the animation should continue. If all conditions are met (not all Frames have been drawn), Invalidate() the Control used to show the animation and, in its Paint event handler, call ImageAnimator.UpdateFrames() to change the current Frame, then e.Graphics.DrawImage() to draw the Image (drawing the Frame that is now the current).

    ▶ As you can see in the visual example, I'm using a Button (btnAnimate) to start the animation. You can move that code to the Form.Shown event handler, if you prefer.
    ▶ I've added a loop counter, in case the animation should loop more than once.

    This is how it visually works:

    ImageAnimator Animate GIF

    Imports System.Drawing.Imaging
    ' [...]
    
    Private animation As Image = My.Resources.icon_confirmation
    Private animationFrames As Integer = 0
    Private currentFrame As Integer = 0
    Private animationMaxLoops As Integer = 1
    Private loops As Integer = 0
    
    Private Sub btnAnimate_Click(sender As Object, e As EventArgs) Handles btnAnimate.Click
        animationFrames = animation.GetFrameCount(New FrameDimension(animation.FrameDimensionsList(0)))
        AnimateImage()
    End Sub
    
    Public Sub AnimateImage()
        If ImageAnimator.CanAnimate(animation) Then
            ImageAnimator.Animate(animation, AddressOf OnFrameChanged)
        End If
    End Sub
    
    Private Sub OnFrameChanged(o As Object, e As EventArgs)
        If currentFrame >= animationFrames Then
            currentFrame = 0
            loops += 1
            If loops >= animationMaxLoops Then
                animationFrames = 0
                loops = 0
                ImageAnimator.StopAnimate(animation, AddressOf OnFrameChanged)
            End If
        Else
            pictureBox1.Invalidate()
            currentFrame += 1
        End If
    End Sub
    
    Private Sub pictureBox1_Paint(sender As Object, e As PaintEventArgs) Handles pictureBox1.Paint
        If animationFrames > 0 Then
            ImageAnimator.UpdateFrames()
            e.Graphics.DrawImage(animation, Point.Empty)
        End If
    End Sub
    

    C# Version

    private Image animation = Properties.Resources.Some_GIF_Image;
    private int animationFrames = 0;
    private int currentFrame = 0;
    private int animationMaxLoops = 1;
    private int loops = 0;
    
    
    private void btnAnimate_Click(object sender, EventArgs e)
    {
        animationFrames = animation.GetFrameCount(new FrameDimension(animation.FrameDimensionsList[0]));
        AnimateImage();
    }
    
    private void AnimateImage()
    {
        if (ImageAnimator.CanAnimate(animation)) {
            ImageAnimator.Animate(animation, OnFrameChanged);
        }
    }
    
    private void OnFrameChanged(object sender, EventArgs e)
    {
        if (currentFrame >= animationFrames) {
            currentFrame = 0;
            loops += 1;
            if (loops >= animationMaxLoops) {
                animationFrames = 0;
                loops = 0;
                ImageAnimator.StopAnimate(animation, OnFrameChanged);
            }
        }
        else {
            pictureBox1.Invalidate();
            currentFrame += 1;
        }
    }
    
    private void pictureBox1_Paint(object sender, PaintEventArgs e)
    {
        if (animationFrames > 0)  {
            ImageAnimator.UpdateFrames();
            e.Graphics.DrawImage(animation, Point.Empty);
        }
    }
    

    A PasteBin of a complete Form that performs the animation using an Image from the Project's Resources.