Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Another approach to animated GIF in WPF, with transparency

0.00/5 (No votes)
31 Oct 2008 1  
Demonstrates the use of animated GIFs in WPF applications.

WpfAnimatedControlScreenshot.jpg

Introduction

In spite of reach support of animation in WPF, I have not found a quick and easy way to insert animated GIFs in a WPF application. After some searching in the net and analyzing the gathered information, I have assumed that every public available solution has one ore more of the following disadvantages:

  1. Use of unsafe code (yes, back to good old BitBlt!)
  2. Thread unsafe.
  3. No or incomplete support of WPF layout, stretching, zoom, etc.
  4. No support for transparency.
  5. Not supported in XAML.
  6. Impossible to use “embedded” resources (only external GIF files supported).
  7. Too complicated and “heavy” design (for example, browser control with dynamically generated content, etc.)

This article presents my trial to solve the problem, taking into account the above mentioned issues. Hope that a future version of the framework brings us such functionality by default.

How it works and design of the code

The main idea is just simple: extend the standard WPF Image control to accept animated GIF, split the GIF into frames, constructing BitmapSources from each frame and keeping them “inside”, then dynamically switching sources to simulate animation.

Let’s look a bit deeper in code: I have created a custom control AnimatedImage, based on the WPF Image control, with an additional Dependency Property AnimatedBitmap of type Bitmap. This saves the full functionality of the original WPF image like scaling, layout, events etc., and makes it easy to set everything directly from XAML. The custom routed event AnimatedBitmapChanged was registered to provide some extra initialization/cleanup during setting the source GIF, if necessary. Actually, the main functionality is concentrated in the following function:

private void UpdateAnimatedBitmap()
{
  int nTimeFrames = AnimatedBitmap.GetFrameCount
        (System.Drawing.Imaging.FrameDimension.Time);
  _nCurrentFrame = 0;
  if (nTimeFrames > 0)
  {
    _BitmapSources = new BitmapSource[nTimeFrames];
    for (int i = 0; i < nTimeFrames; i++)
    {
      AnimatedBitmap.SelectActiveFrame(
           System.Drawing.Imaging.FrameDimension.Time, i);
      Bitmap bitmap = new Bitmap(AnimatedBitmap);
      bitmap.MakeTransparent();
      _BitmapSources[i] = 
        System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(
         bitmap.GetHbitmap(),
         IntPtr.Zero,
         Int32Rect.Empty,
         System.Windows.Media.Imaging.BitmapSizeOptions.FromEmptyOptions());
     }
     StartAnimate();
   }
}

Here, the frames are retrieved from the source GIF, then each frame is made transparent, converted to a BitmapSource, and saved into an internal buffer. Finally, we call the StartAnimate() helper to start the animation process. For controlling animation, an internal ImageAnimator control is used, with the ChangeSource() callback, doing nothing but just switching the active source of the Image control and sending an update appearance request. The BeginInvoke used here is for thread safety.

private delegate void VoidDelegate();

private void OnFrameChanged(object o, EventArgs e)
{
  Dispatcher.BeginInvoke(DispatcherPriority.Render, 
        new VoidDelegate(delegate { ChangeSource(); }));
}

void ChangeSource()
{
  Source = _BitmapSources[_nCurrentFrame++];
  _nCurrentFrame = _nCurrentFrame % _BitmapSources.Length;
  ImageAnimator.UpdateFrames();
}

As I mentioned, there are also two helpers StartAnimate() and StopAnimate() and a property IsAnimating to give the basic control over the AnimatedImage.

public void StopAnimate()
{
  if (_bIsAnimating)
  {
    ImageAnimator.StopAnimate(AnimatedBitmap, 
         new EventHandler(this.OnFrameChanged));
    _bIsAnimating = false;
  }
}

public void StartAnimate()
{
  if (!_bIsAnimating)
  {
    ImageAnimator.Animate(AnimatedBitmap, 
                  new EventHandler(this.OnFrameChanged));
    _bIsAnimating = true;
  }
}

private bool _bIsAnimating;
public bool IsAnimating
{
   get { return _bIsAnimating; }
}

Using the code

Follow steps 1a or 1b and then 2 to use this custom control in a XAML file.

Step 1a. Using this custom control with sources:

  • Add the WpfAnimatedControl project to your workspace.
  • Add the XmlNamespace attribute to the root element of the markup file where it is to be used:
  • xmlns:MyNamespace="clr-namespace:WpfAnimatedControl"

Step 1b. Using this custom control as DLL:

Add the XmlNamespace attribute to the root element of the markup file where it is to be used:

xmlns:MyNamespace="clr-namespace:WpfAnimatedControl;assembly=WpfAnimatedControl"

You will also need to add a project reference from the project where the XAML file lives to this assembly, and Rebuild to avoid compilation errors.

Step 2. Go ahead and use your control in the XAML file.

To add AnimatedImage to a window, insert the following line to your window XAML file, for example, as shown below:

< MyNamespace:AnimatedImage Name="aimg" 
    AnimatedBitmap="{x:Static code:Resources.wrong}" 
    Stretch="None" AnimatedBitmapChanged="aimg_AnimatedBitmapChanged"/>

Here we assume that you already insertedthe GIF in the window resources.

The provided test application (VS2008, Framework 3.5) demonstrates the common use of the control, and also setting the GIF from an external file.

Warning

Be careful to set the Source property together with the AnimatedBitmap property from XAML; generally, it was designed to use Source only if no animation runs. I.e., if you need a static picture, better use the original WPF Image, and do not set the Source if you are using the AnimatedImage control.

Conclusion

This is only a very basic implementation of an animated image control, but could save a lot of time if you need to “just show a transparent animated GIF”. Feel free to extend it with extra validation, functionality, and so on.

History

  • 31.10.2008: Initial release.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here