WPF 加载和显示 GIF 图片的完整指南

发布于:2025-07-20 ⋅ 阅读:(14) ⋅ 点赞:(0)

WPF 加载和显示 GIF 图片的完整指南

在 WPF 中加载和显示 GIF 图片需要一些特殊处理,因为 WPF 的 Image 控件默认不支持动画 GIF。

解决方案一:使用 WpfAnimatedGif 库(推荐)

这是最简单且功能最完整的方法。

实现步骤:

  1. 安装 NuGet 包
    在 NuGet 包管理器中安装 WpfAnimatedGif

    Install-Package WpfAnimatedGif
    
  2. 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>
    
  3. 代码后台

    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 图片有以下几种方法:

  1. 推荐方案:使用 WpfAnimatedGif NuGet 包

    • 优点:功能完整、支持透明背景、易于使用
    • 缺点:需要添加外部依赖
  2. 自定义解码器

    • 优点:无外部依赖
    • 缺点:实现复杂、功能有限
  3. 使用 MediaElement

    • 优点:内置支持
    • 缺点:不支持透明背景、功能有限

对于大多数应用场景,推荐使用 WpfAnimatedGif 库,它提供了最完整的 GIF 支持,包括:

  • 播放控制(播放、暂停、停止)
  • 速度调整
  • 播放次数控制
  • 透明背景支持
  • 事件通知(如帧改变、播放完成等)

通过本文提供的代码示例,您可以轻松地在 WPF 应用中实现 GIF 加载和播放功能,并根据需要添加自定义控制功能。


网站公告

今日签到

点亮在社区的每一天
去签到