To save a visual to an image file need to use RenderTargetBitmap, detail is reference to Save and read images in WPF.

But sometimes you will find the output image was shifted or left blank. This is because that RenderTargetBitmap render the visual object based on cordinate of its parent object. Margin of itself, Padding or BorderThickness of its parent will all affect the rendered image. Although I think this is a bug of WPF, it seems the feature is by design as reference to RenderTargetBitmap layout offset influence.

There are three ways to fix the problem,

Solution Description
Add a Border Simple, but the visual logical tree is changed.
Use a VisualBrush Maintain the original visaul logical tree, but need to do more process.
Temporary change the reative postion by Measure() and Arrange() Need to change back after rendering, and the two function is automatically called while repainting by WPF, so the real process is hard to tell.

The first solution is simplist. RenderTargetBitmap is only shifted by the distance between visaul and its parent object, so you just need to add a fake parent and move the margin to the parent object.

If the original visual logical tree is like

<Grid>
<Canvas Margin="20" />
</Grid>

changed to

<Grid>
<Border Margin="20">
<Canvas />
</Border>
</Grid>

and just call the method of SaveTo as Save and read images in WPF.

private void SaveTo(Visual v, string f)
{
/// get bound of the visual
Rect b = VisualTreeHelper.GetDescendantBounds(v);

/// new a RenderTargetBitmap with actual size of c
RenderTargetBitmap r = new RenderTargetBitmap(
(int)b.Width, (int)b.Height,
96, 96, PixelFormats.Pbgra32);

/// render visual
r.Render(v);

/// new a JpegBitmapEncoder and add r into it
JpegBitmapEncoder e = new JpegBitmapEncoder();
e.Frames.Add(BitmapFrame.Create(r));

/// new a FileStream to write the image file
FileStream s = new FileStream(f,
FileMode.OpenOrCreate, FileAccess.Write);
e.Save(s);
s.Close();
}

Second solution is draw the visaul to a DrawingVisual object, and pass the object to the SaveTo function.

private DrawingVisual ModifyToDrawingVisual(Visual v)
{
/// new a drawing visual and get its context
DrawingVisual dv = new DrawingVisual();
DrawingContext dc = dv.RenderOpen();

/// generate a visual brush by input, and paint
VisualBrush vb = new VisualBrush(v);
dc.DrawRectangle(vb, null, b);
dc.Close();

return dv;
}

PS. The context will act after calling Close(). You can use the using statement to block the region. And the SaveTo method should be modified as

/// render visual
r.Render(ModifyToDrawingVisual(v));

The third solution is to temporarily change the reative postion by Measure and Arrange before Render,

private void ModifyPosition(FrameworkElement fe)
{
/// get the size of the visual with margin
Size fs = new Size(
fe.ActualWidth +
fe.Margin.Left + fe.Margin.Right,
fe.ActualHeight +
fe.Margin.Top + fe.Margin.Bottom);

/// measure the visual with new size
fe.Measure(fs);

/// arrange the visual to align parent with (0,0)
fe.Arrange(new Rect(
-fe.Margin.Left, -fe.Margin.Top,
fs.Width, fs.Height));
}

PS. The solution is only suitable for UIElement, and need to change the position back after rendering.

private void ModifyPositionBack(FrameworkElement fe)
{
/// remeasure a size smaller than need, wpf will
/// rearrange it to the original position
fe.Measure(new Size());
}

Because the size to be measured is smaller then the real size, WPF will rearrange the layout and align the position back. And the render part is modified as

/// render visual
ModifyPosition(v as FrameworkElement);
r.Render(v);
ModifyPositionBack(v as FrameworkElement);

About Measure() and Arrange(), detail is reference toUIElement.Measure Method

--
Reference
RenderTargetBitmap layout offset influence
RenderTargetBitmap tips
RenderTargetBitmap and XamChart - Broken, Redux

Problem of saving images in WPF (RenderTargetBitmap)zz的更多相关文章

  1. wpf RenderTargetBitmap保存控件为图片时图片尺寸不对的问题

    使用RenderTargetBitmap渲染图片时,必须在需要渲染的控件外层包含一个border控件,不然渲染的图片返回就会出现问题. 如下: <Grid> <Grid.RowDef ...

  2. WPF:如何高速更新Model中的属性

    原文:[WPF/MVVM] How to deal with fast changing properties In this article, I will describe a problem w ...

  3. ACM/ICPC 2018亚洲区预选赛北京赛站网络赛 A、Saving Tang Monk II 【状态搜索】

    任意门:http://hihocoder.com/problemset/problem/1828 Saving Tang Monk II 时间限制:1000ms 单点时限:1000ms 内存限制:25 ...

  4. 导出程序界面(UI)到图片

    无意间看到这个需求,查阅了相关文章,有两篇不错的博客给出了解决方案,地址如下: 1.在WPF程序中将控件所呈现的内容保存成图像 2.随心所欲导出你的 UI 界面到 PDF 文件 主要使用的接口: Si ...

  5. 随心所欲导出你的 UI 界面到 PDF 文件

    使用 C1PDF 控件可以导出文件到 PDF 文件,结合 .NET 平台特性你可以在任何客户端生成自定义报表.你可以打印任何 UI 界面,例如 DataGrid 导出到 PDF. 在本篇文章中我们将阐 ...

  6. TensorFlow 生成 .ckpt 和 .pb

    原文:https://www.cnblogs.com/nowornever-L/p/6991295.html 1. TensorFlow  生成的  .ckpt 和  .pb 都有什么用? The . ...

  7. Emacs及扩展配置

    Emacs及扩展配置 Table of Contents 1. 动机之反思 2. 它山之石 3. 扩展的管理 4. 我额外安装的通用扩展(在purcell基础上) 5. LaTex相关的问题和配置 6 ...

  8. 客户端类中中记录异常的方法: 使用Log4net

    1.首先引用Log4Net 的命名空间 using log4net; 2.在使用的类中生命静态变量 log public class FileService    {        static re ...

  9. COM Error Code(HRESULT)部分摘录

    Return value/code Description 0x00030200 STG_S_CONVERTED The underlying file was converted to compou ...

随机推荐

  1. C/C++头文件使用 #ifndef #define #endif 的原因

    背景 在编译的时候,出现"redefine"的错误,最后检查才发现对应的头文件没有写正确的预编译信息: #ifndef _HeadFileName_H #define _HeadF ...

  2. vs写python扩展资料收集

    总结: 1.创建dll工程: 2.增加包含头文件路径 :将python路径下的include加入到包含头文件路径:在工程属性页 C/C++/附加包含目新增<Python>\include目 ...

  3. How the problem solved about " Dealing with non-fast-forward errors"

    Recently ,I got confused When I use  git to push one of my project. The problem is below: And I Foun ...

  4. 我的js函数库(持续更新)

    常用js初始化函数 function id(obj) { return document.getElementById(obj); } function bind(obj, ev, fn) { if ...

  5. jdbc链接数据库的三种方式

    /** * jdbc连接数据库 * @author APPle * */ public class Demo1 { //连接数据库的URL private String url = "jdb ...

  6. mysql character set exception

    问题: 插入数据时,报了这样一个错误:“_mysql_exceptions.Warning: Incorrect string value: ‘\xE6\xB5\x81\xE8\xA1\x8C…’ f ...

  7. MAC上向GitHub上上传自己的项目

    1首先需要在github上创建你自己的github账户 2新建仓库(注意选择initialize this repository with a README) 3生成ssh(公钥.pub&私钥 ...

  8. web项目中各种路径的获取

    以工程名为/DemoWeb为例: 访问的jsp为:http://localhost:8080/DemoWeb/test/index.jsp 1 JSP中获得当前应用的相对路径和绝对路径 (1)得到工程 ...

  9. xcode 8 重新支持插件

    苹果出了Xcode8之后,就加了签名让之前的自定义插件无法继续的安装使用.想要重新使用插件的话只要用自己的签名覆盖苹果的签名即可. 1.创建自签名证书 钥匙串->钥匙串访问->证书助理-& ...

  10. c# 保存数据到txt (追加)

    StringBuilder sb = new StringBuilder(); sb.AppendLine(DateTime.Now.ToString("yyyy-MM-dd hh:mm:s ...