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

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;}// 播放 GIFprivate void PlayGif(object sender, RoutedEventArgs e){ImageBehavior.SetAnimatedSource(gifImage, new BitmapImage(new Uri("Assets/loading.gif", UriKind.Relative)));}// 暂停 GIFprivate void PauseGif(object sender, RoutedEventArgs e){ImageBehavior.GetAnimator(gifImage)?.Pause();}// 恢复播放private void ResumeGif(object sender, RoutedEventArgs e){ImageBehavior.GetAnimator(gifImage)?.Play();}// 停止 GIFprivate void StopGif(object sender, RoutedEventArgs e){ImageBehavior.GetAnimator(gifImage)?.Dispose();gifImage.Source = null;}// 切换 GIFprivate 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 加载和播放功能,并根据需要添加自定义控制功能。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/web/89655.shtml
繁体地址,请注明出处:http://hk.pswp.cn/web/89655.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Node.js GET/POST请求详解

Node.js GET/POST请求详解 引言 Node.js作为一种基于Chrome V8引擎的JavaScript运行环境&#xff0c;以其高性能、非阻塞I/O模型和轻量级等特点&#xff0c;在服务器端开发中得到了广泛应用。本文将详细介绍Node.js中GET和POST请求的处理方法&#xff0c;帮助开发者更好地理解和…

C++string类(2)

3.string类对象的访问及遍历操作函数名称功能说明operator[] &#xff08;重 点&#xff09;返回pos位置的字符&#xff0c;const string类对象调用beginendbegin获取第一个字符的迭代器 end获取最后一个字符下一个位置的迭代器rbeginrendrbegin获取最后一个字符的迭代器 ren…

SQLShift:一款异构数据库存储过程迁移工具

SQLShift 是一款专注于解决企业级数据库迁移难题的智能 SQL 方言转换平台&#xff0c;尤其擅长异构数据库存储过程的自动化迁移。 SQLShift 工具深度融合了 AI 与 SQL 语法专家模型&#xff0c;可以大幅提升迁移效率并降低人工适配风险。 功能特性 多源多目标&#xff1a;目前…

学习设计模式《十八》——备忘录模式

一、基础概念 备忘录模式的本质是【保存和恢复内部状态】。 备忘录模式的思考序号备忘录模式的思考说明1保存是手段&#xff0c;恢复才是目的标准的备忘录模式保存数据的手段是通过内存缓存&#xff1b;广义的备忘录模式实现的时候&#xff0c;可以采用离线存储的方式&#xff…

HOT100——排序篇Leetcode215. 数组中的第K个最大元素

文章目录题目&#xff1a;Leetcode215. 数组中的第K个最大元素原题链接思路1代码1思路2代码2题目&#xff1a;Leetcode215. 数组中的第K个最大元素 原题链接 数组中的第K个最大元素 思路1 排序 排序后返回倒数第k个数 代码1 思路2 使用priority_queue&#xff0c;大根堆&#x…

三维重建一: 相机几何

参考这位大佬&#xff1a;https://zhuanlan.zhihu.com/p/458000359 一. 基本的投影模型 正如上面所说&#xff0c;相机是一个将三维物体投影为二维图像的设备。 对于小孔相机&#xff0c;或者薄透镜相机来说&#xff0c;基础投影的数学模型可以表达为 我们把这个过程表达在笛…

mysql 字符集不一致导致索引失效问题

mysql 字符集不一致导致索引失效问题 问题&#xff1a; 两张表&#xff0c;同一个字段&#xff0c;由于字符集不一致&#xff0c;导致虽然都有索引&#xff0c;在关联查询时&#xff0c;索引失效身份表 identity_info &#xff0c;查询索引信息 show index from identity_info …

Linux内核设计与实现 - 第6章 内核数据结构

目录1. 链表 (Linked Lists)2. 队列 (Queues)3. 映射 (Maps)4. 二叉树 (Binary Trees)5. 位图 (Bitmaps)6. 其他数据结构性能考量1. 链表 (Linked Lists) 单向链表 vs 双向链表 struct list_head 标准实现内核链表API&#xff1a;LIST_HEAD(), list_add(), list_del() 环形链表…

十五、K8s可观测能力:日志收集

十五、K8s可观测能力&#xff1a;日志收集 文章目录十五、K8s可观测能力&#xff1a;日志收集1、云原生日志框架-ECK介绍1.1 什么是ECK&#xff1f;1.2 ECK核心资源&#xff1a;1.3 生产级日志收集架构2、日志收集-ECK2.1 集群规划2.2 ECK 安装2.3 一键部署高可用 ES 集群2.4 一…

微服务变更?自动化测试利器Parasoft SOAtest修复快、准、稳!

微服务架构凭借灵活和可扩展的优势越来越普及&#xff0c;但随之而来的变更也成了开发团队的“心头大患”。服务之间依赖复杂&#xff0c;接口改来改去&#xff0c;不仅让开发更费劲&#xff0c;还容易导致测试用例失效、测试效率下降&#xff0c;甚至埋下系统不稳的隐患。 自…

将Android Studio创建的一个apk工程放到Android15源码中构建

背景描述&#xff1a;起初Android Studio创建的apk工程&#xff0c;为了方便系统版本发布和后期维护需要同时支持两种构建方式&#xff1a;Android Studio Gradle构建 IDE界面环境&#xff0c;对习惯java环境变成的友好、UI设计方便看效果Android15系统构建时自动构建 So…

yolo8目标检测+训练(识别和平精英人物)

✅步骤一&#xff1a;安装 PyTorch&#xff08;M1 专用&#xff09;# 推荐使用官方 MPS 后端&#xff08;Apple Metal 加速&#xff09; pip install torch torchvision torchaudio确认是否使用了 Apple MPS&#xff1a;import torch print(torch.backends.mps.is_available()…

【ArcGISPro】修改conda虚拟安装包路径

问题在ArcGISPro中经常使用克隆&#xff0c;导致C盘默认虚拟安装包内存越来越大&#xff0c;导致电脑很卡解决方案打开ArcGISPro所在conda文件夹D:\Program Files\ArcGIS\Pro\bin\Python\Scripts打开命令行工具&#xff08;如 CMD 或终端&#xff09;。输入以下命令&#xff0c…

三格电子—西门子PLC串口转网口模块

一、功能概述本文档是西门子PLC串口转以太网系列产品&#xff0c;包含SG-S7-200-ETH、S7-200-ETH(2P)&#xff0c;SG-S7-300-ETH&#xff0c;SG-S7-300-ETH(2P)共四个产品。使用框图如下图所示意。1.1 产品功能本系列产品用来给西门子S7-200/300 PLC串口扩展出网口来&#xff0…

Python—requests模块

Python requests 模块代码演示 requests 是 Python 中一个简单易用的 HTTP 库&#xff0c;用于发送各种 HTTP 请求。下面是一些常见的使用示例&#xff1a; 1. 基本 GET 请求 import requests# 发送 GET 请求 response requests.get(https://api.github.com)# 检查请求是否成功…

华为仓颉编程语言语法简介与示例

华为仓颉编程语言语法简介与示例 仓颉编程语言是华为自主研发的新一代通用编程语言&#xff0c;由南京大学冯新宇教授团队主导设计&#xff0c;于 2024 年华为开发者大会&#xff08;HDC&#xff09;正式发布&#xff0c;并在 2025 年 7 月推出首个长期支持版本&#xff08;LTS…

触发器的创建

- 建立product表&#xff0c;操作方式operate表要求1.定义触发器实现在产品表(product)中每多一个产品,就在操作表(operate)中记录操作方式和时间以及编号记录。注&#xff1a;操作说明&#xff1a;标记执行delete 、insert、update2.定义触发器实现在产品表(product)中每更新一…

论文略读: RASA: RANK-SHARING LOW-RANK ADAPTATION

ICLR 2025尽管 LoRA 有诸多优势&#xff0c;但近期研究表明&#xff0c;它在大规模训练数据集和复杂任务&#xff08;如数学推理和代码生成&#xff09;中&#xff0c;仍然落后于全参数微调&#xff08;FFT&#xff09;一个合理的解释是&#xff1a;低秩约束限制了 LoRA 的表达…

VSCode - VSCode 查找中文字符

VSCode 查找中文字符 1、查找中文字符使用快捷键 Ctrl F 打开查找框点击正则表达式按钮 .*在搜索框中输入 [\u4e00-\u9fa5]&#xff0c;这个范围涵盖了基本的中文字符2、查找中文字符串使用快捷键 Ctrl F 打开查找框点击正则表达式按钮 .*在搜索框中输入 [\u4e00-\u9fa5]&a…

SQL基础操作指南:增删改查入门

前言 在日常数据库操作中&#xff0c;掌握增删查改是每个开发者必备的技能。下面我将通过实例解析SQL的核心操作要点&#xff0c;帮你避开常见陷阱。一、新增数据&#xff08;INSERT&#xff09; 单行插入&#xff1a;明确指定字段和值 INSERT INTO 表名(字段1, 字段2) VALUES …