原文地址:https://www.cnblogs.com/younShieh/p/11279420.html

前文

  项目中遇到一个难题,需要将上百个没有显示出来的Canvas存储为图片保存在本地。

  操作步骤应该是将Canvas转换成位图,然后将位图转换成本地图片。所以一步一步的来吧。


位图转本地文件

  查阅资料后(百度一下)后得知把位图转换为本地图片进行保存可以通过BitmapSource来进行转换,通过PngBitmapEncoder() 来实现。具体代码如下:

//path为保存路径
using (FileStream outStream = new FileStream(path, FileMode.Create))
{
PngBitmapEncoder encoder = new PngBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(bitmapSource));
encoder.Save(outStream);
}

Canvas转位图

   如何将位图转换成图片的方法知道了,接下来就是要把Canvas转换成Bitmap了。RenderTargetBitmap() 方法就可以实现Visual对象到位图的转换。查看Canvas是继承于Visual的,所以理论上应该没问题。尝试了一下,确实如此。代码如下:

 RenderTargetBitmap bitmap = new RenderTargetBitmap((int)canvas.ActualWidth, (int)canvas.ActualHeight, 96, 96, PixelFormats.Pbgra32);
bitmap.Render(canvas);

Canvas测量和定位

  理论上到这里就已经完了,但是我的Canvas并没有显示出来,导致ActualWidth和ActualHeight两个属性值都为0,且不能通过设置大小改变其实际尺寸,这就有点抠脑壳了。那就继续查资料吧。对于ActualWidth和ActualHeight这两个属性,MSDN如是说:

此属性是基于其他宽度输入和布局系统的计算的值。

值由布局系统本身基于实际呈现的传递,设置,因此可能稍微小于属性的设置值如Width是作为输入更改的基础。

因为ActualWidth是计算后的值,应注意可能有多次或递增的报告更改为它作为各种操作结果由布局系统。布局系统可能会计算子元素所需的测量空间、父元素的约束等。



  MSDN的机翻虽然前言不搭后语,但是大致能明白是什么意思。也就是说ActualWidth和ActualHeight是不能设置的,是通过实际呈现来自动计算出来的。但是我的Canvas没有呈现出来啊,就不能被动计算出来了,这可怎么办呢。。。不可以被动计算,就只有主动去设置,主动计算这条路可以走了。。。又是查阅资料后,还是发现了有路可走的。

https://stackoverflow.com/questions/5189139/how-to-render-a-wpf-usercontrol-to-a-bitmap-without-creating-a-window

  通过学习大佬们的经验得知,没有显示的界面也是可以转换成位图的,主要是需要去测量Measure() 和定位Arrange() 来设置Canvas的位置和大小,通过这两个方法可以形成递归布局更新。也就是说通过这两个方法,设置了父元素为子元素计算的最终大小,也就是实际的尺寸。

canvas.Measure(new Size(300, 300));
canvas.Arrange(new Rect(new Size(300,300)));

内存的优化

  1. 理论上到这里就已经完了,但是我的Cancas们还有一个先决条件,就是他们有很多。(问题确实有点多。。。)我需要保存的Canvas数量太多,所以不负众望,我的内存爆了(要爆啦~)。但是我已经做了自动回收,没理由啊。 我尝试查找了很多地方的的可能会出现的问题,毫不吝啬的使用 GC.Collect(); Dispose(); using(),异步,并行,多线程 等等等方法,但是却没什么卵用。。。扣破脑壳,一段代码一段代码的屏蔽,分布排查到底是哪里的问题。我以为是位图转换导致的内存泄露,但是分步调试后发现其实并不怎么消耗内存,而且都做了合理的回收。慢慢调试后我发现原来是我的测量Measure() 和定位Arrange() 这两个方法占用的大量的内存,而且应该是没有回收到。



  2. 首先我想的是我能不能不用这两个方法。因为我的界面不能显示,所以我要把他放到内存里面去渲染,如果不渲染的话,直接设置RenderTargetBitmap() 的 尺寸得到的是空白的图(试过了。。)。查了很久的资料后,确实是没有找到合适的办法,能做到对不显示的图片不用Arrange() 方法就能转换成位图。所以这两个方法我必须要用,所以也不能投机取巧了,只能硬着头皮上了。



  3. baidu、google轮番上阵,MSDN、stackoverflow各路齐飞,,,都没有找到合适的办法解决。或许没有遇到我这么特殊情况的人吧,也有可能是我的搜索方式有问题。不过,正在我扣破脑壳之际,我想到会不会Canvas 也有对应的Dispose() 方法呢?(原谅我已经晕了,Dispose()只能用于继承于IDisposable类”的知识点早已飞到九霄云外)不过,Canvas确实没有的Dispose() 方法,,,伤心,绝望。那会不会有Arrange() 对应的 Dispose() 方法呢?尝试在Canvas后输入Arrage,得到了一个 InvalidateArrange() 方法:

使元素排列状态(布局)无效。 排列状态失效后,该元素将更新其布局,更新将以异步方式发生,除非随后由 System.Windows.UIElement.UpdateLayout强制执行。

  使元素排列状态(布局)无效,不就是释放布局占用的内存资源了?尝试了一下,果然如此。内存占用情况再也不是“一行白鹭上青天”了。。。唉,内存爆了的问题就此得以解决,妈妈再也不用担心我的软件崩溃啦~~~

附代码如下:

canvas.Measure(new System.Windows.Size(1920, 1080));
canvas.Arrange(new Rect(0, 0, 1920, 1080));
renderBitmap.Render(canvas);
canvas.InvalidateArrange();
canvas.InvalidateMeasure();
canvas.UpdateLayout();



由此得到的教训是,代码还是得自己敲。。。不然掉到坑你都不知道怎么爬出来。。。

打完收工。

【WPF】大量Canvas转换为本地图片遇到的问题的更多相关文章

  1. PHP将Base64图片转换为本地图片并保存

    本文出至:新太潮流网络博客 /** * [将Base64图片转换为本地图片并保存] * @E-mial wuliqiang_aa@163.com * @TIME 2017-04-07 * @WEB h ...

  2. HTML5 Canvas显示本地图片实例1、Canvas预览图片实例1

    1.前台代码: <input id="fileOne" type="file" /> <canvas id="canvasOne&q ...

  3. WPF中Image显示本地图片(转)

    private void SetSource(System.Windows.Controls.Image image, string fileName) { System.Drawing.Image ...

  4. html5上传本地图片,在线预览及裁剪(filereader,canvas)

    1 我们常常需要上传头像,点击上传按钮时候需要预览一下,使用filereader方法无需和后台交互,代码如下: //本地图片在上传之前的预览效果 //图片上传预览 function previewIm ...

  5. 【基于WPF+OneNote+Oracle的中文图片识别系统阶段总结】之篇一:WPF常用知识以及本项目设计总结

    篇一:WPF常用知识以及本项目设计总结:http://www.cnblogs.com/baiboy/p/wpf.html 篇二:基于OneNote难点突破和批量识别:http://www.cnblog ...

  6. WPF技巧-Canvas转为位图

    转自:http://www.cnblogs.com/tmywu/archive/2010/09/14/1825650.html 在WPF中我们可以将Canvas当成一种画布,将Canvas中的控件当成 ...

  7. 移动端H5选择本地图片

    移动端H5选择本地图片 html://input<input type="file" accept="image/*" capture="cam ...

  8. 【基于WPF+OneNote+Oracle的中文图片识别系统阶段总结】之篇二:基于OneNote难点突破和批量识别

    篇一:WPF常用知识以及本项目设计总结:http://www.cnblogs.com/baiboy/p/wpf.html 篇二:基于OneNote难点突破和批量识别:http://www.cnblog ...

  9. CANVAS运用-对图片的压缩上传(仅针对移动浏览器)

    最近在移动端设计头像上传功能时,原本是以<input type="file">直接通过formData上传,然而实际使用情况是:对于过大的图片(高像素手机所拍摄的照片等 ...

随机推荐

  1. 【Webkit Blog翻译】深入研究WebRTC | 内有福利

    本文译自 A Closer Look Into WebRTC” 我们在最近的一篇 WebKit博客中宣布了对High Sierra平台和iOS中Safari的WebRTC支持.现在,我们希望能够带领大 ...

  2. Android开发需要了解的 IM 知识

    引言 即便在通讯如此发达的今天,IM 也依然是诸多场景下非常重要的基础能力.因此做为 一名 Android 开发,不可避免的会遇到一些IM 相关的需求或问题.本文以一个Android开发的角度来讲述I ...

  3. spring 5.x 系列第2篇 —— springmvc基础 (代码配置方式)

    文章目录 一.搭建hello spring工程 1.1 项目搭建 1.2 相关注解说明 二.配置自定义拦截器 三.全局异常处理 四.参数绑定 4.1 参数绑定 4.2 关于日期格式转换的三种方法 五. ...

  4. Spring 之Aop实现日志记录

    Aop实现见代码,简单demo实现 package com.idcos.automate.config; import com.idcos.automate.dal.auto.dao.dcos.Dco ...

  5. Java学习笔记——String类型转换

    一滴水里观沧海,一粒沙中看世界 ——一带一路欢迎宴致辞 上代码: package cn.stringtoobj; public class TypeConversion { public static ...

  6. 【HDU - 2612】Find a way

    -->Find a way 直接上Chinese  Descriptions: hsj和lsh最近迷上了pokemon go的游戏.在双十一大物期中考试来临之前,他们想抓一只稀有土拨鼠来攒攒人品 ...

  7. python初识(3)

    bool 字符串 for循环 bool 数字非零全都是True 字符串非空全都是True 字符串 索引 从0开始 0 切片选取 [x:y] 左闭右开区间 [x:y:z] 选取x到y之间 每隔z选取一次 ...

  8. epoll使用详解:epoll_create、epoll_ctl、epoll_wait、close

    epoll - I/O event notification facility 在linux的网络编程中,很长的时间都在使用select来做事件触发.在linux新的内核中,有了一种替换它的机制,就是 ...

  9. MQ初窥门径【面试必看的Kafka和RocketMQ存储区别】

    MQ初窥门径 全称(message queue)消息队列,一个用于接收消息.存储消息并转发消息的中间件 应用场景 用于解决的场景,总之是能接收消息并转发消息 用于异步处理,比如A服务做了什么事情,异步 ...

  10. 嵊州D3T2 福尔贝斯太太的快乐夏日 summer

    宗教,或是无节制的自由主义,是致人腐化的毒剂. 现在,一个人要经历 n 个事件,编号为 1 ∼ n. 经历 x 号事件,他的危险值就会增加 x. 一开始他的危险值是 0. 当一个人的危险值大于 0 且 ...