WPF GIF 动画






4.89/5 (10投票s)
本文介绍了在您的 WPF 项目中播放 GIF 动画的最佳方法。
引言
我想我们很多人都知道,标准的 WPF 不支持动画 GIF 文件。目前有几种方法可以解决这个问题,但它们中的许多都有自己的缺点 - 或者占用大量的 CPU 时间,或者占用大量的资源。我设计并实现了自己的解决方案。
背景
我的解决方案依赖于使用 System.Threading.Timer
类,它可以帮助我组织帧的顺序。但是每一帧都有它自己的延迟值。我决定通过使用 ParseGif
类解析 GIF 文件来读取它。所以,结果只是持续的更改帧,预先冻结它们以避免内存泄漏。 更多关于如何防止内存泄漏的信息,您可以在这篇精彩的文章中找到 - Finding Memory Leaks in WPF-based applications。

Using the Code
让我们来看看。

我们的解决方案分为两个项目 - 一个是动画类项目,另一个是功能测试项目。 我想专注于项目测试。 我没有添加一个动画文件 - 它不会显示任何东西。 我决定在 RichTextBox
中添加 100 个笑脸,在这种情况下,我认为这是演示性能的最佳选择。

所以,这是加载动画的主要函数 LoadSmile
。
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;
}
然后,当您更改图像时,将执行处理函数。 在其中,我们正在执行和分解 GIF 文件并启动计时器,计时器会更改帧。
private void UpdateAnimatedBitmap()
{
try
{
int nTimeFrames = GetFramesCount(); //get frames count
_nCurrentFrame = 0; //Set current frame to default value
if (nTimeFrames > 0) //this is animated file
{
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); //Initialize timer
StartAnimate(); //start animation
}
else //this is single image
{
Bitmap bitmap = new Bitmap(AnimatedBitmap);
_BitmapSources = new List<bitmapsource>(1);
_BitmapSources.Add(CreateBitmapSourceFromBitmap(bitmap));
Source = _BitmapSources[0];
}
}
catch { }
}
在本文开头的附加项目中,您可以找到在此代码中执行的一些函数。 如您所见,决策的架构不是很困难。 我们可以使用内置的 Windows Forms 类 ImageAnimator
,但作为 WPF 中大型解决方案的实践,此类不太适合。
我认为这个模型最引人注目的例子是聊天程序。 通常,必须同时强制显示数十个笑脸。 自然,为了客户的舒适,这不应该占用大量的资源和 CPU 时间。
最后,我想展示包含帧序列的代码。
private void OnFrameChanged(object obj)
{
try
{
Dispatcher.BeginInvoke(DispatcherPriority.Render,
new VoidDelegate(delegate { ChangeSource(); }));
}
catch { }
}
void ChangeSource()
{
try
{
timer.Change(Delays[_nCurrentFrame]
* 10, 0); //get the current delay for the frame
Source = _BitmapSources[_nCurrentFrame++]; //make it visible
_nCurrentFrame = _nCurrentFrame %
_BitmapSources.Count; //compute the next frame index
}
catch { }
}
此外,我们的类实现了 IDisposable
接口,该接口允许您在不需要笑脸时释放资源。
public void Dispose()
{
try
{
timer.Change(-1, -1);
timer.Dispose();
_BitmapSources.Clear();
Source = null;
}
catch { }
}
原则上,我想您会理解此代码的主要思想。
在附加的文件中,您可以找到其余代码和一个非常简单的测试项目。
结论
在本文中,我试图描述不仅是显示动画文件的可能性,而且展示了创建能够真正承受高压和巨大经济资源并避免内存泄漏的代码的可能性。