WPF 加载和显示 GIF 图片的完整指南
在 WPF 中加载和显示 GIF 图片需要一些特殊处理,因为 WPF 的 Image
控件默认不支持动画 GIF。
解决方案一:使用 WpfAnimatedGif 库(推荐)
这是最简单且功能最完整的方法。
实现步骤:
安装 NuGet 包:
在 NuGet 包管理器中安装WpfAnimatedGif
:Install-Package WpfAnimatedGif
XAML 实现:
<Window x:Class="GifDemo.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:gif="http://wpfanimatedgif.codeplex.com" mc:Ignorable="d" Title="GIF 动画演示" Height="450" Width="800"> <Grid> <Grid.RowDefinitions> <RowDefinition Height="*"/> <RowDefinition Height="Auto"/> </Grid.RowDefinitions> <!-- 加载 GIF 图片 --> <Image x:Name="gifImage" gif:ImageBehavior.AnimatedSource="Assets/loading.gif" gif:ImageBehavior.RepeatBehavior="Forever" Stretch="Uniform" HorizontalAlignment="Center" VerticalAlignment="Center"/> <!-- 控制面板 --> <StackPanel Grid.Row="1" Orientation="Horizontal" HorizontalAlignment="Center" Margin="10"> <Button Content="开始" Click="PlayGif" Margin="5" Padding="10,5"/> <Button Content="暂停" Click="PauseGif" Margin="5" Padding="10,5"/> <Button Content="恢复" Click="ResumeGif" Margin="5" Padding="10,5"/> <Button Content="停止" Click="StopGif" Margin="5" Padding="10,5"/> <ComboBox x:Name="gifSelector" Margin="10,0" Width="150" SelectionChanged="GifSelectionChanged"> <ComboBoxItem Content="加载动画" Tag="Assets/loading.gif"/> <ComboBoxItem Content="庆祝动画" Tag="Assets/celebration.gif"/> <ComboBoxItem Content="进度动画" Tag="Assets/progress.gif"/> </ComboBox> </StackPanel> </Grid> </Window>
代码后台:
using System.Windows; using WpfAnimatedGif; namespace GifDemo { public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); // 设置默认选择 gifSelector.SelectedIndex = 0; } // 播放 GIF private void PlayGif(object sender, RoutedEventArgs e) { ImageBehavior.SetAnimatedSource(gifImage, new BitmapImage( new Uri("Assets/loading.gif", UriKind.Relative))); } // 暂停 GIF private void PauseGif(object sender, RoutedEventArgs e) { ImageBehavior.GetAnimator(gifImage)?.Pause(); } // 恢复播放 private void ResumeGif(object sender, RoutedEventArgs e) { ImageBehavior.GetAnimator(gifImage)?.Play(); } // 停止 GIF private void StopGif(object sender, RoutedEventArgs e) { ImageBehavior.GetAnimator(gifImage)?.Dispose(); gifImage.Source = null; } // 切换 GIF private void GifSelectionChanged(object sender, RoutedEventArgs e) { if (gifSelector.SelectedItem is ComboBoxItem item && item.Tag is string gifPath) { var source = new BitmapImage(new Uri(gifPath, UriKind.Relative)); ImageBehavior.SetAnimatedSource(gifImage, source); } } } }
解决方案二:使用自定义 GIF 解码器
如果你不想使用第三方库,可以使用自定义实现:
using System;
using System.IO;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Media.Imaging;
using System.Drawing;
using System.Drawing.Imaging;
namespace GifDemo
{
public partial class MainWindow : Window
{
private Bitmap _gifBitmap;
private BitmapSource[] _gifFrames;
private int _currentFrame;
private bool _isPlaying;
private readonly DispatcherTimer _animationTimer = new DispatcherTimer();
public MainWindow()
{
InitializeComponent();
_animationTimer.Tick += NextFrame;
}
private void LoadGif(string path)
{
// 清理现有资源
StopGif();
// 加载新 GIF
_gifBitmap = new Bitmap(path);
// 获取帧数
int frameCount = _gifBitmap.GetFrameCount(FrameDimension.Time);
_gifFrames = new BitmapSource[frameCount];
// 提取所有帧
for (int i = 0; i < frameCount; i++)
{
_gifBitmap.SelectActiveFrame(FrameDimension.Time, i);
_gifFrames[i] = ToBitmapSource(_gifBitmap);
}
// 获取帧延迟
var frameDelay = GetFrameDelay(_gifBitmap);
_animationTimer.Interval = TimeSpan.FromMilliseconds(frameDelay);
// 显示第一帧
_currentFrame = 0;
gifImage.Source = _gifFrames[0];
// 开始播放
PlayGif();
}
private int GetFrameDelay(Bitmap gif)
{
const int PropertyTagFrameDelay = 0x5100;
// 获取帧延迟属性
var delayProperty = gif.GetPropertyItem(PropertyTagFrameDelay);
// 默认延迟 (100ms)
if (delayProperty == null) return 100;
// 返回第一帧的延迟(以毫秒为单位)
return BitConverter.ToInt32(delayProperty.Value, 0) * 10;
}
private BitmapSource ToBitmapSource(Bitmap bitmap)
{
using (var memory = new MemoryStream())
{
bitmap.Save(memory, ImageFormat.Png);
memory.Position = 0;
var bitmapImage = new BitmapImage();
bitmapImage.BeginInit();
bitmapImage.StreamSource = memory;
bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
bitmapImage.EndInit();
bitmapImage.Freeze();
return bitmapImage;
}
}
private void NextFrame(object sender, EventArgs e)
{
if (!_isPlaying || _gifFrames == null) return;
_currentFrame = (_currentFrame + 1) % _gifFrames.Length;
gifImage.Source = _gifFrames[_currentFrame];
}
private void PlayGif(object sender = null, RoutedEventArgs e = null)
{
_isPlaying = true;
_animationTimer.Start();
}
private void PauseGif(object sender, RoutedEventArgs e)
{
_isPlaying = false;
_animationTimer.Stop();
}
private void StopGif(object sender = null, RoutedEventArgs e = null)
{
_isPlaying = false;
_animationTimer.Stop();
_currentFrame = 0;
gifImage.Source = _gifFrames?[0];
}
protected override void OnClosed(EventArgs e)
{
base.OnClosed(e);
_animationTimer.Stop();
_gifBitmap?.Dispose();
}
}
}
解决方案三:使用 MediaElement(适用于简单 GIF)
对于不需要透明背景的 GIF,可以使用 MediaElement:
<Window x:Class="GifDemo.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="GIF 动画演示" Height="450" Width="800">
<Grid>
<!-- 使用 MediaElement 播放 GIF -->
<MediaElement x:Name="mediaElement"
Source="Assets/loading.gif"
LoadedBehavior="Play"
UnloadedBehavior="Stop"
Stretch="Uniform"
HorizontalAlignment="Center"
VerticalAlignment="Center"/>
<!-- 控制面板 -->
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" VerticalAlignment="Bottom" Margin="10">
<Button Content="播放" Click="PlayGif" Margin="5" Padding="10,5"/>
<Button Content="暂停" Click="PauseGif" Margin="5" Padding="10,5"/>
<Button Content="停止" Click="StopGif" Margin="5" Padding="10,5"/>
</StackPanel>
</Grid>
</Window>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void PlayGif(object sender, RoutedEventArgs e)
{
mediaElement.Play();
}
private void PauseGif(object sender, RoutedEventArgs e)
{
mediaElement.Pause();
}
private void StopGif(object sender, RoutedEventArgs e)
{
mediaElement.Stop();
}
}
常见问题解决方案
1. GIF 不播放
- 检查文件路径是否正确
- 确保 GIF 文件已设置为 “Resource” 或 “Content” 生成操作
- 验证 GIF 文件是否损坏(用其他软件打开测试)
2. 透明背景显示为黑色
- 使用支持透明背景的解决方案(如 WpfAnimatedGif)
- 确保 GIF 本身支持透明
- 在 Image 控件上设置
Background="Transparent"
3. 性能问题
- 避免加载过多 GIF
- 暂停不可见的 GIF
- 降低 GIF 分辨率
- 使用
BitmapCache
提高渲染性能:<Image.CacheMode> <BitmapCache EnableClearType="True" RenderAtScale="1" /> </Image.CacheMode>
4. 控制 GIF 播放次数
<Image gif:ImageBehavior.AnimatedSource="animation.gif"
gif:ImageBehavior.RepeatBehavior="3x"/> <!-- 播放3次 -->
总结
在 WPF 中加载和显示 GIF 图片有以下几种方法:
推荐方案:使用
WpfAnimatedGif
NuGet 包- 优点:功能完整、支持透明背景、易于使用
- 缺点:需要添加外部依赖
自定义解码器:
- 优点:无外部依赖
- 缺点:实现复杂、功能有限
使用 MediaElement:
- 优点:内置支持
- 缺点:不支持透明背景、功能有限
对于大多数应用场景,推荐使用 WpfAnimatedGif
库,它提供了最完整的 GIF 支持,包括:
- 播放控制(播放、暂停、停止)
- 速度调整
- 播放次数控制
- 透明背景支持
- 事件通知(如帧改变、播放完成等)
通过本文提供的代码示例,您可以轻松地在 WPF 应用中实现 GIF 加载和播放功能,并根据需要添加自定义控制功能。