While reviewing The C# Top 100 it occurred to me that in my own code base I have many bits of code which may make useful blog posts, and that shouldn't take as long to write as the ones I usually create. Plus, I've a fair amount of source code for extending built in controls in various ways, creating new controls from scratch and other useful library code - I need to explore ways of decoupling some of that and releasing it for anyone to use.

To get started with this idea is a simple article on painting animated images using C#. If you assign an animated GIF file to the Image property of a Control, the .NET Framework will take care of animating the image for you. However, it only provides this automatically for the Image property and not for other properties such as BackgroundImage, or any custom image properties you add to your own components.

Fortunately the framework doesn't secret the animation functionality away and provides the static ImageAnimator class (located in System.Drawing) to handle the bulk of the work.

The demonstration program with an animated image
The demonstration program with an animated image

(Image Credit: Dominique Toussaint)

Checking if an image can be animated

The first thing to do is check if an animate can be animated. While calling the various ImageAnimator methods with static images (or even null references) won't crash, if your image is static then there's no need to call them in the first place.

The ImageAnimator.CanAnimate method takes a source Image and returns if it supports animation or not. Passing null to this method will also return false.

csharp
private Image _image;
private bool _isAnimating;

_image = LoadImageFromSomewhere();
_isAnimating = ImageAnimator.CanAnimate(_image);
The demonstration program with a static image
The demonstration program with a static image

(Image Credit: Vera Kratochvil)

Preparing for animation

If your image supports animation, the next step is to call ImageAnimator.Animate, passing in both the source image and an EventHandler to receive change notifications.

This method will create a background thread that will periodically check watched images and advance frames as required. When it detects a new frame should be painted, it will call the event handler registered for the image, allowing you to handle the update, e.g. repaint your control.

Only one thread is created no matter how many images are being animated

csharp
private void SetupAnimation()
{
  _isAnimating = ImageAnimator.CanAnimate(_image);

  if (_isAnimating)
  {
    ImageAnimator.Animate(_image, this.OnFrameChangedHandler);
  }
}

private void OnFrameChangedHandler(object sender, EventArgs eventArgs)
{
  // trigger a repaint of our image
  customPaintPanel.Invalidate();
}

One mistake I sometimes see developers do is calling the Refresh method of a custom Windows Forms control. Calling Refresh will force the control and its children to be repainted immediately. An alternative way is to call Invalidate (without any arguments) which will mark the window to be repainted without forcing the paint or repainting child windows - generally this is more suitable and reduces the number of unneeded repaints.

Halting animation

When you are finished with the source image, you should call ImageAnimator.StopAnimate to remove the image and callback from the list of watched images.

csharp
private void CleanUp()
{
  if (_image != null)
  {
    // disable animations
    if (_isAnimating)
    {
      ImageAnimator.StopAnimate(_image, this.OnFrameChangedHandler);
    }

    _isAnimating = false;

    _image.Dispose();
    _image = null;
  }
}

Painting the image

The current frame is part of the Image instance's metadata, so you don't need to do anything specific to paint animated images vs static. With that said, the ImageAnimator class tracks the current frame separately and doesn't update the source Image until requested, which you do by calling ImageAnimator.UpdateFrames.

csharp
private void customPaintPanel_Paint(object sender, PaintEventArgs e)
{
  if (_isAnimating)
  {
    ImageAnimator.UpdateFrames(_image);
  }

  e.Graphics.DrawImage(_image, 0, 0);
}

Wrapping up

As you can probably see, this is quite a simple process and makes it easy to support animated graphics in applications that reference System.Drawing.

A complete example demonstrating how to use the ImageAnimator class is available from the link below.

Update History

  • 2017-10-28 - First published
  • 2020-11-22 - Updated formatting

Like what you're reading? Perhaps you like to buy us a coffee?

Donate via Buy Me a Coffee

Donate via PayPal


Files


Comments