WPF的Image控件BitmapImage以及Uri的资源占用问题

来源:互联网 发布:海尔电视直播软件下载 编辑:程序博客网 时间:2024/06/09 19:31
通过BitmapImage的Clone方法,保持使用Uri,同样可以解决问题。
   场景:
  1. WPF下用Image控件展示图片;
  2. 控件的图片源自然选用BitmapImage; 
  3. BitmapImage通过Uri对象指向磁盘的某个文件。

  显示正常,但是这时候如果我们再有别的地方要操作这个磁盘文件,比如程序中或者其他地方,就会说资源已被占用,这个网友大师们已经发现了这个问题,并提出了解决方案。
见:http://www.silverlightchina.net/html/study/WPF/2010/1021/2806.html

 

   关于BitmapImage的CacheOption属性可以参考这里:http://www.cnblogs.com/nio-nio/archive/2011/05/11/2043622.html

  • 正常: Image-<- BitmapImage -<-Uri -<- PNG Image File(@disk)
  • 问题: PNG ImageFile(@disk) 将不能被其他操作使用,Uri无法释放资源。

  • 方法1:Image-<- BitmapImage -<-FileStream -<- PNG ImageFile(@disk)
  • 该方法使用FileStream代替Uri,FileStream是一个实现了IDisposable的文件处理流,使用后我们可以用它的Close方法或者用using代码块来释放它。
  • 问题:过早释放资源将使Image控件中的图片不可见,必须维持一个FileStream的引用,以便随时释放。

  • 方法2: Image-<- BitmapImage -<-byte[] -< BinaryReader <-PNG ImageFile(@disk)
  • 该方法通过BinaryReader将图片文件转化成byte数组存在内存中,这样我们就可以放弃文件,因为图片数据已然进内存了。通过byte数组生成的BitmapImag已跟文件无关,故源文件可以在别的操作中继续使用。
  • 这种方法可以说是一个比较好的解决方案。

 

  • 方法3:(BitmapImage)bmp.CacheOption=BitmapCacheOption.OnLoad
  • 该方法指定了BitmapImage中图片的缓存方式,不过我感觉OnLoad同样会有问题。

   行了,说准备下班吧,事来了,调试的时候发现,该死的其他控件也会通过这个Image控件拿到图片,并借助其Source属性拿到图片的来源(比如Uri,字节数组),并序列化出来到数据库以保存,这是个绘图板,可以把Image拖进来的那种。
   可想而知,Uri中包含的图片文件可以快速使图片再次被加载,字节数组直接报错,转换异常。起初百思不得其解,后来看了数据库,又看了绘图板的控件库,才发现,看来必须用Uri解决问题,开始思考最初的方法哪里出了问题。

   我们把最初的方法分开写,
    Uri uri =new Uri(文件路径,枚举.相对还是绝对路径);
    BitmapImagebmp = new BitmapImage(uri);
   tempImage.Source = bmp; // 控件

   下面依次注释这些语句,再写个方法去访问图片文件,看看是谁占用了资源,最终发现其实是控件没有释放掉资源,Uri只是作为定位对象指引了一个路径,BitmapImage则做了一层封装。
好吧,.net的GC是不靠谱的,不要妄图通过tempImage.Source =null或者System.GC.SuppressFinalize(tempImage)释放资源,尤其是急的时候。

   想想为什么,因为Image的源直接维持了对包装有图片文件的BitmapImage的引用,不过很庆幸,BitmapImage提供了Clone方法,可以完成对其的深拷贝,立马试试:
tempImage.Source = bmp.Clone();

   OK,成功了,图片显示正常,其他操作也不报错,这给了我们新的选择,因为用Uri引入的图片可以再其他适当的时候通过Image图片的Source属性,再次拿到他的UriString,然后不难再还原为Uri对象,进一步对文件进行操作,十分方便。

  • 方法: Image-< BitmapImage.Clone<- BitmapImage -<- Uri-<- PNG Image File(@disk)
  • 优势:不但解决了资源占用问题,而且在Image控件中留下了文件的“痕迹”(其实是BitmapImage留下的),以便进一步操作文件。
  • 验证:我们选择一个图片文件,然后让代码以该方法指向它,运行程序,待程序显示图片后,尝试删除这个图片文件,成功;若不加Clone方法,则会提示资源已被占用,说明该方法有效。
  • 注意:这个方法同样会有一定的时间延迟,因为要从Image.Source与BitmapImage.Clone的“绑定”到本身BitmapImage源对象被GC回收,释放对文件的占用,是需要时间的,不想要等的话方法2字节数组很好用的。 
  • 其他:还有更狠的呢,由于我们不能过度(或者说是自由)干涉.net的垃圾收集机制及其垃圾收集器GC,这个BitmapImage什么时候会被放掉,也不由我们决定,这是一件很头大的事情,因为放早了同样会导致Image控件里的图片不显示,为啥...你Source指向的内存区域都不是bmp了,.net说不能用,必须不显示啊...妥协方法我在第二天补充了http://blog.sina.com.cn/s/blog_3f2c72ea0100ss69.html。

   这样Image的解决方案就比较完整了,我也在关注其他更完美的方法。


    其实我很高兴能在实习中实践自己所学的知识,并加以细化,毕竟.net这东西我接触也不到一年,用起来也都是小打小闹,但是这次通过实习新学习WPF真是长进不少。

1 0
原创粉丝点击