Click here to Skip to main content
Click here to Skip to main content

Another approach to animated GIF in WPF, with transparency

By , 31 Oct 2008
Rate this:
Please Sign up or sign in to vote.

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, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Lecha

Germany Germany
No Biography provided

Comments and Discussions

 
QuestionUse of pack uri Pinmemberbhaash19-Sep-13 22:52 
BugIgnores the frame's display interval. Pinmembertehcrash21-Jul-12 19:08 
GeneralMy vote of 5 PinmemberRupesh Kumar Swami17-Mar-12 0:45 
QuestionSpeed problem PinmemberMember 471711425-Feb-12 18:40 
Generalthanx Pinmembermanura198526-Jan-12 20:41 
GeneralMy vote of 5 PinmemberMember 471711414-Jan-12 18:16 
GeneralMy vote of 5 PinmemberGuenter Wolf26-Apr-11 10:24 
GeneralError 18 Unknown build error, 'Key cannot be null Pinmemberpevans432114-Nov-10 14:43 
GeneralLeaking? [modified] Pinmemberchaf270128-Apr-09 5:51 
GeneralThreading issues... Pinmemberrlrcstr6-Mar-09 17:19 
GeneralRe: Threading issues... PinmemberLecha6-Apr-09 1:49 
GeneralThank you Lecha. Here is a modified version to support both animated and stills. [modified] Pinmemberfnorum2-Jan-09 7:34 
GeneralRe: Thank you Lecha. Here is a modified version to support both animated and stills. [modified] PinmemberMember 471711416-Feb-12 7:40 
GeneralRe: Thank you Lecha. Here is a modified version to support both animated and stills. [modified] PinmemberMember 471711417-Feb-12 2:07 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web02 | 2.8.140415.2 | Last Updated 31 Oct 2008
Article Copyright 2008 by Lecha
Everything else Copyright © CodeProject, 1999-2014
Terms of Use
Layout: fixed | fluid