在使用WPF的Image时候,从硬盘加载大位图(1M以上)会使应用假死(卡顿)几秒钟,如果连续加载的话假死(卡)的时间更长。也很容易出现内存溢出。
初步分析,问题应该是出在文件的加载和渲染上。渲染上得耗时不多,重点是加载位图到内存的操作了。加载的操作是封装好的,kyler 没有去找内部实现去详细思考问题的原因,了解了几个实例位图的方式后。发现了可以通过流来实例并显示位图。
就想到,如果程序通过异步加载文件2进制流。然后在内存中实例化Image 就不会出现这个问题了。
理论是这样,至于是否真的能达到理想效果。写个实例出来自然就知道了。
首先是写一个测试的程序(个人习惯,会先用控件摆个界面。思考要使用的点,然后组织好界面后就开始实现逻辑)
主类代码:
--------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Media.Imaging;
using System.IO;
using System.Threading;
using System.Windows;
using System.Windows.Threading;
namespace kyler.image
{
/// <summary>
/// 图片加载进度事件
/// </summary>
/// <param name="sender">加载者对象</param>
/// <param name="byteLoaded">已经加载的byte数</param>
/// <param name="byteTotile">总长度</param>
public delegate void LoadProgress(ImageThreadLoad sender,long byteLoaded,long byteTotile);
/// <summary>
/// 图片加载完成
/// </summary>
/// <param name="sender">加载者对象</param>
public delegate void LoadComplete(ImageThreadLoad sender);
/// <summary>
/// 加载状态枚举
/// </summary>
public enum ImageLoadStatus {
/// <summary>
/// 已准备好,可以加载图片
/// </summary>
Ready,
/// <summary>
/// 图片加载中
/// </summary>
Progress,
/// <summary>
/// 图片已经加载完成
/// </summary>
Complete
}
/// <summary>
/// 异步图片加载类
/// </summary>
public class ImageThreadLoad : DependencyObject
{
/// <summary>
/// 错误消息,在结束时间中如果没获取到对象。请查看该对象以了解错误原因
/// </summary>
public String ErrorMessage;
/// <summary>
/// 当前加载的图片原始地址
/// </summary>
public String ImagePath;
/// <summary>
/// 缩略图的限制大小
/// </summary>
public Size MaxSize = new Size(100,100);
/// <summary>
/// 加载完成事件
/// </summary>
public event LoadComplete Complete;
/// <summary>
/// 加载进度事件
/// </summary>
public event LoadProgress Progress;
public static DependencyProperty StatusProperty = DependencyProperty.Register("Status", //属性名称
typeof(ImageLoadStatus),
//属性类型
typeof(ImageThreadLoad),
//该属性所有者,即将该属性注册到那个类上
new PropertyMetadata(ImageLoadStatus.Ready)); //属性默认值
public ImageLoadStatus Status { get { return (ImageLoadStatus)GetValue(StatusProperty); } set { SetValue(StatusProperty, value); } }
//public ImageLoadStatus Status { get; set ; } ;
public static DependencyProperty LengthProperty = DependencyProperty.Register("Length", //属性名称
typeof(long),
//属性类型
typeof(ImageThreadLoad),
//该属性所有者,即将该属性注册到那个类上
new PropertyMetadata(0L)); //属性默认值
public long Length { get { return (long)GetValue(LengthProperty); } set { SetValue(LengthProperty, value); } }
public static DependencyProperty ByteLoadedProperty = DependencyProperty.Register("ByteLoaded", //属性名称
typeof(long),
//属性类型
typeof(ImageThreadLoad),
//该属性所有者,即将该属性注册到那个类上
new PropertyMetadata(0L)); //属性默认值
public long ByteLoaded { get { return (long)GetValue(ByteLoadedProperty); } set { SetValue(ByteLoadedProperty, value); } }
public ImageThreadLoad() {
thread = new Thread(new ThreadStart(runLoad));
Status = ImageLoadStatus.Ready;
}
MemoryStream ms = null;//图像文件的内存流
Thread thread = null;//异步进程
/// <summary>
/// 获取用于显示的 BitmapImage 对象,原始尺寸。
/// </summary>
public BitmapImage Image
{
get
{
if (ms == null ¦¦ Status != ImageLoadStatus.Complete)
{
return null;
}
BitmapImage BI = null;
lock (ms)
{
BI = new BitmapImage();
ms.Seek(0, SeekOrigin.Begin);
BI.BeginInit();
BI.StreamSource = ms;
BI.EndInit();
}
return BI;
}
}
/// <summary>
/// 获取缩略图,默认不大于100,如要设置尺寸请在获取前设置MaxSize属性
/// </summary>
public BitmapImage ThumbnaiImage
{
get
{
if (ms == null ¦¦ Status != ImageLoadStatus.Complete)
{
return null;
}
BitmapImage BI = null;
lock (ms)
{
ms.Seek(0, SeekOrigin.Begin);
System.Drawing.Image fullSizeImg = System.Drawing.Image.FromStream(ms);
int filewidth = 0;
int fileheight = 0;
if (fullSizeImg.Height <= fullSizeImg.Width)
{
filewidth = (int)MaxSize.Width;
fileheight = fullSizeImg.Height /(fullSizeImg.Width / (int)MaxSize.Width) ;
}
else
{
fileheight = (int)MaxSize.Height;
filewidth = fullSizeImg.Width / (fullSizeImg.Height / (int)MaxSize.Height);
}
System.Drawing.Image nimag = fullSizeImg.GetThumbnailImage(filewidth, fileheight,null,System.IntPtr.Zero);
BI = new BitmapImage();
System.IO.MemoryStream tms = new System.IO.MemoryStream();
nimag.Save(tms, System.Drawing.Imaging.ImageFormat.Png);// 格式自处理,这里用 bitmap
// 下行,初始一个 ImageSource 作为 myImage 的Source
nimag.Dispose();
fullSizeImg.Dispose();
BI.BeginInit();
BI.StreamSource = tms;
BI.EndInit();
}
return BI;
}
}
/// <summary>
/// 清除所有资源占用并销毁线程内存流等对象,解除事件绑定。
/// </summary>
public void Destroy() {
if (thread != null && thread.IsAlive)
{
thread.Abort();
}
if (ms != null) {
ms.Close();
ms = null;
}
if (Complete != null) {
Delegate[] dlist = Complete.GetInvocationList();
foreach (Delegate d in dlist) {
this.Complete -= (LoadComplete)d;
}
}
if (Progress != null)
{
Delegate[] dlist = Progress.GetInvocationList();
foreach (Delegate d in dlist)
{
this.Progress -= (LoadProgress)d;
}
}
}
/// <summary>
/// 加载指定本地图片。
/// </summary>
/// <param name="path">本地地址,相对或绝对地址</param>
public void LoadImage(String path) {
ImagePath = path;
FileInfo fileinfo = new FileInfo(path);
if (fileinfo.Exists)
{
Length = fileinfo.Length;
if (Status == ImageLoadStatus.Ready)
{
Status = ImageLoadStatus.Progress;
thread.Start();
}
else if (Status == ImageLoadStatus.Complete)
{
//sendComplete();
Application.Current.Dispatcher.
BeginInvoke(DispatcherPriority.
Normal, (ThreadStart)delegate()
{ sendComplete(); });
}
}
else {
ErrorMessage = "文件不存在!";
Application.Current.Dispatcher.
BeginInvoke(DispatcherPriority.
Normal, (ThreadStart)delegate()
{ sendComplete(); });
}
}
/// <summary>
/// 触发完成事件。完成不一定是加载成功,发生错误而失败也会触发完成事件。
/// 具体查看ErrorMessage来获取错误信息
/// </summary>
private void sendComplete()
{
Status = ImageLoadStatus.Complete;
if (Complete != null) {
Complete.Invoke(this);
}
}
void runLoad()
{
ms = new MemoryStream();
FileStream fs = new FileStream(ImagePath, FileMode.Open);
byte[] buff = new byte[2048*30];
long size = 0;
int readCount = 0;
while ((readCount = fs.Read(buff, 0, buff.Length)) != 0)
{
size += readCount;
// Console.WriteLine("Read:" + readCount + ",All Size:" + size);
ms.Write(buff, 0, readCount);
Thread.Sleep(10);
try
{
Application.Current.Dispatcher.
BeginInvoke(DispatcherPriority.
Normal, (ThreadStart)delegate()
{ sendProgress(size); });
}
catch (Exception ex) {
//
}
}
try
{
Application.Current.Dispatcher.
BeginInvoke(DispatcherPriority.
Normal, (ThreadStart)delegate()
{ sendComplete(); });
}
catch (Exception ex)
{
//
}
}
/// <summary>
/// 发送当前的进度
/// </summary>
/// <param name="size"></param>
private void sendProgress(long size)
{
ByteLoaded = size;
if (Progress != null) {
Progress.Invoke(this,ByteLoaded,Length);
}
}
}
}
-------------------------
使用方法
---------------------------------
ImageThreadLoad itl = new ImageThreadLoad();
itl.Progress += new LoadProgress(itl_Progress); //事件侦听-加载进度
itl.Complete += new LoadComplete(itl_Complete);//事件侦听-完成事件
itl.LoadImage(fileName); //加载
----------------------------------
事件侦听处理函数
-------------------------------------
void itl_Complete(ImageThreadLoad sender)
{
//image1.Source = sender.Image; // 获取加载的原图
//sender.Length // 加载位图的文件大小
//sender.MaxSize = new Size(150, 150); // 缩略图最大尺寸
//image.Source = sender.ThumbnaiImage; // 获取缩略图
}
void itl_Progress(ImageThreadLoad sender, long byteLoaded, long byteTotile)
{
//progressBar1.Maximum = byteTotile; // 总字节大小
//progressBar1.Value = byteLoaded; // 已加载大小
}
--------------------------------------
至此,代码已经全部结束了。
希望能给予一定的帮助。谢谢。