Introduction
As I think many of us know, a standard set of WPF does not support animated GIF files. At the moment there are several approaches to solving this problem, but many of them have their own drawbacks - or take up a lot of CPU time, or take up a lot of resources. I designed and implemented its decision.
Background
My solution depends on working with System.Threading.Timer
class, which helps me to organize succession of frames. But each frame has its own delay value. I decide to read it by parsing the GIF file with ParseGif
class. So, the result is only consistent change frames, pre-freeze them in order to avoid memory leaks. More on how to prevent memory leaks, you can find in this remarkable article - Finding Memory Leaks in WPF-based applications.
Using the Code
Let's take a look.
Our solution is divided into two projects - a project of the class of animation, and project testing capabilities. I want to concentrate on the project testing. I did not add that there is one animation file - it would not display anything. I decided to add 100 smileys in the RichTextBox
, which in this context, I think is the best option for demonstrating performance.
So, here is the main function LoadSmile
of loading animation:
private static void OnAnimatedBitmapChanged
(DependencyObject obj, DependencyPropertyChangedEventArgs args)
{
AnimatedImage control = (AnimatedImage)obj;
control.UpdateAnimatedBitmap();
RoutedPropertyChangedEventArgs<bitmap> e =
new RoutedPropertyChangedEventArgs<bitmap>(
(Bitmap)args.OldValue,
(Bitmap)args.NewValue, AnimatedBitmapChangedEvent);
control.OnAnimatedBitmapChanged(e);
}
public static readonly RoutedEvent AnimatedBitmapChangedEvent =
EventManager.RegisterRoutedEvent(
"AnimatedBitmapChanged", RoutingStrategy.Bubble,
typeof(RoutedPropertyChangedEventHandler<bitmap>), typeof(AnimatedImage));
...
public void LoadSmile(Bitmap bitmap)
{
this.AnimatedBitmap = bitmap;
}
Then when you change the image, processing function is executed. In it, we are performing and disassembling GIF file and start the timer, which change the frames.
private void UpdateAnimatedBitmap()
{
try
{
int nTimeFrames = GetFramesCount(); _nCurrentFrame = 0; if (nTimeFrames > 0) {
MemoryStream stream = new MemoryStream();
AnimatedBitmap.Save(stream, System.Drawing.Imaging.ImageFormat.Gif);
stream.Seek(0, SeekOrigin.Begin);
byte[] buffer = new byte[stream.Length];
stream.Read(buffer, 0, buffer.Length);
ParseGif(buffer);
_BitmapSources = new List<bitmapsource>(nTimeFrames);
stream.Dispose();
FillBitmapSources(nTimeFrames);
timer = new Timer(OnFrameChanged, null, -1 , -1); StartAnimate(); }
else {
Bitmap bitmap = new Bitmap(AnimatedBitmap);
_BitmapSources = new List<bitmapsource>(1);
_BitmapSources.Add(CreateBitmapSourceFromBitmap(bitmap));
Source = _BitmapSources[0];
}
}
catch { }
}
In the attached project at the beginning of the article, you can find some of the functions carried out in this code. As you can see, the architecture of the decision is not very hard. We could use the built-in Windows Forms class ImageAnimator
, but as practice for large-scale solutions in WPF, this class is poorly suited.
I think the most striking example of this model is the program for a chat. Often, it must be enforced at the same time tens of smiles. Naturally, this should not upload a lot of resources and CPU time for the comfort of the client.
Finally, I think of bringing the code in which there is a succession of frames.
private void OnFrameChanged(object obj)
{
try
{
Dispatcher.BeginInvoke(DispatcherPriority.Render,
new VoidDelegate(delegate { ChangeSource(); }));
}
catch { }
}
void ChangeSource()
{
try
{
timer.Change(Delays[_nCurrentFrame]
* 10, 0); Source = _BitmapSources[_nCurrentFrame++]; _nCurrentFrame = _nCurrentFrame %
_BitmapSources.Count; }
catch { }
}
Also, our class implemented IDisposable
interface which allows you to free resources when smile is unneeded:
public void Dispose()
{
try
{
timer.Change(-1, -1);
timer.Dispose();
_BitmapSources.Clear();
Source = null;
}
catch { }
}
In principle, I think you will understand the main idea of this code.
In the attached file, you can find the rest of the code and a very easy project for testing.
Conclusion
In this article, I tried to describe the possibility of not displaying an animated file somehow, but to show the possibility of creating a code that can truly withstand high pressures and great economical resource, avoiding memory leaks.