原文:Taking WPF “Screenshots”

I was recently working on a Surface project at Microsoft (that will be shown at BETT  ) and one of the requirements was to provide an external “administration console”.  As part of that console I wanted to show an “screenshot” of the current game running on the Surface unit; after playing around for a while it turned out it was pretty straightforward.

We did consider sending over the RAW XAML from the Surface to the Console, but that would potentially have issues when resources weren’t available, so the approach that was taken was to create a JPG screenshot and send it over as a byte array via WCF.

Rendering to a BitmapFrame

The key to this approach is RenderTargetBitmap which allows us to render any WPF Visual to a BitmapFrame as follows:

RenderTargetBitmap renderTarget = new RenderTargetBitmap(, , , , PixelFormats.Pbgra32);
renderTarget.Render(myVisual);
BitmapFrame bitmapFrame = BitmapFrame.Create(renderTarget);

Then from there we can use JpegBitmapEncoder to create a JPG from that BitmapFrame:

JpegBitmapEncoder jpgEncoder = new JpegBitmapEncoder();
jpgEncoder.Frames.Add(bitmapFrame);

Then we can output that JPG to a stream of our choice using the Save() method.

Problems

While this works for many cases, and indeed worked perfectly for the Surface application, we do encounter problems when the source we are rendering has Transforms applied or when it’s not positioned at 0,0.  When this occurs the screenshots we take will have the content shifted“out of frame” resulting in black borders, or content missing altogether.  The following screenshot demonstrates the problem:

Workaround

To work around the problem we can use a VisualBrush to “draw” our source element onto a new Visual, and render that with our RenderTargetBitmap:

DrawingVisual drawingVisual = new DrawingVisual();
DrawingContext drawingContext = drawingVisual.RenderOpen();
using (drawingContext)
{
drawingContext.DrawRectangle(sourceBrush, null, new Rect(new Point(, ), new Point(, )));
}
renderTarget.Render(drawingVisual);

It’s not ideal, but I’ve yet to find a better workaround for it.

Putting it all Together

To make it more useful, we can wrap the whole lot up into a Extension Method.  Rather than extending Visual, I’ve chosen to use UIElement so I have access to the RenderSize to calculate the required size of the output bitmap.  I’ve also included parameters to scale the resulting bitmap and to set the JPG quality level:

///
/// Gets a JPG "screenshot" of the current UIElement
///
/// UIElement to screenshot
/// Scale to render the screenshot
/// JPG Quality
/// Byte array of JPG data public static byte[] GetJpgImage(this UIElement source, double scale, int quality)
{
double actualHeight = source.RenderSize.Height;
double actualWidth = source.RenderSize.Width; double renderHeight = actualHeight * scale;
double renderWidth = actualWidth * scale; RenderTargetBitmap renderTarget = new RenderTargetBitmap((int) renderWidth, (int) renderHeight, , , PixelFormats.Pbgra32); VisualBrush sourceBrush = new VisualBrush(source); DrawingVisual drawingVisual = new DrawingVisual(); DrawingContext drawingContext = drawingVisual.RenderOpen(); using (drawingContext)
{
drawingContext.PushTransform(new ScaleTransform(scale, scale)); drawingContext.DrawRectangle(sourceBrush, null, new Rect(new Point(, ), new Point(actualWidth, actualHeight)));
} renderTarget.Render(drawingVisual); JpegBitmapEncoder jpgEncoder = new JpegBitmapEncoder(); jpgEncoder.QualityLevel = quality; jpgEncoder.Frames.Add(BitmapFrame.Create(renderTarget)); Byte[] _imageArray; using (MemoryStream outputStream = new MemoryStream()) { jpgEncoder.Save(outputStream); _imageArray = outputStream.ToArray(); } return _imageArray;
}

WPF 对控件进行截图且不丢失范围(转载)的更多相关文章

  1. wpf 对控件进行截图,获取快照

    有时候我们项目,在执行某个操作后,会生成一些数据结果,如报表一类的东西,我们需要对结果进行保存,甚至是生成word文档. 那么首先获取到控件快照就最基本的条件. 生成快照的静态方法类 using Sy ...

  2. WPF常用控件应用demo

    WPF常用控件应用demo 一.Demo 1.Demo截图如下: 2.demo实现过程 总体布局:因放大缩小窗体,控件很根据空间是否足够改变布局,故用WrapPanel布局. <ScrollVi ...

  3. WPF开源控件扩展库 - MaterialDesignExtensions

    Material Design Extensions 在WPF开源控件库 Material Design in XAML Toolkit(本站介绍:链接)的基础上进行了控件扩展和特性新增.本开源项目中 ...

  4. C# WPF开源控件库:MahApps.Metro

    其实站长很久之前就知道这个开源WPF控件库了,只是一直欣赏不了这种风格,但也star了该项目.每次浏览该仓库时,发现star越来越多,也看到很多网友对它的褒奖,所以今天就向大家推荐这款WPF控件库. ...

  5. WPF Popup 控件导致被遮挡内容不刷新的原因

    WPF Popup 控件导致被遮挡内容不刷新的原因 周银辉 今天在写一个WPF控件时用到了Popup控件,很郁闷的情况是:当popup关闭时,原来被popup挡住的界面部分不刷新,非要手动刷新一下(比 ...

  6. 创建 WPF 工具箱控件

    创建 WPF 工具箱控件 WPF (Windows Presentation Framework) 工具箱控件模板允许您创建 WPF 控件,会自动添加到 工具箱 安装扩展的安装. 本主题演示如何使用模 ...

  7. wpf打印控件 实现分页打印控件功能

    因为 要实现打印 wpf  listbox控件  数据特别多 要打印在 几张纸上    找了几天 都没有找到相关的例子 现在 解决了 在这里和大家分享一下 public void print(Fram ...

  8. WPF 分页控件 WPF 多线程 BackgroundWorker

    WPF 分页控件 WPF 多线程 BackgroundWorker 大家好,好久没有发表一篇像样的博客了,最近的开发实在头疼,很多东西无从下口,需求没完没了,更要命的是公司的开发从来不走正规流程啊, ...

  9. WPF Image控件中的ImageSource与Bitmap的互相转换

    原文:WPF Image控件中的ImageSource与Bitmap的互相转换  1.从bitmap转换成ImageSource [DllImport("gdi32.dll", ...

随机推荐

  1. Linux查看本机IP:curl cip.cc

    curl http://members.3322.org/dyndns/getip curl ip.6655.com/ip.aspx curl ifconfig.me curl icanhazip.c ...

  2. C#编写COM组件

    1.新建一个类库项目 2.将Class1.cs改为我们想要的名字 问是否同时给类改名,确定 3.修改Properties目录下面的AssemblyInfo.cs ComVisible属性设置为True ...

  3. 033_linux操作系统火焰图探测系统性能

    火焰图是定位疑难杂症的神器,比如 CPU 占用高.内存泄漏等问题.特别是 Lua 级别的火焰图,可以定位到函数和代码级别. 一.研究 https://moonbingbing.gitbooks.io/ ...

  4. webservice:com.sun.xml.internal.ws.server.ServerRtException: [failed to localize]

    发布webservice发生了错误,一直没有能够解决,错误如下: Exception in thread "main" com.sun.xml.internal.ws.server ...

  5. Ex 7_17 考虑如下的网络(其中数字为对应边的容量)...第十三次作业

    (a) 利用ford-fulkerson算法即可求出最大流和最小分割. (b) 剩余网络为 由S可达的顶点为A.B.可达T的顶点为C. (c) 瓶颈边有e(A,C),e(B,C). (d) 下图中不包 ...

  6. Arrays.asList()vs Collections.singletonList()

    Collections.singletonList(something)是不可变的, 对Collections.singletonList(something)返回的列表所做的任何更改将导致Unsup ...

  7. 22)django-中间件

    一:中间件介绍 django 中的中间件(middleware),在django中,中间件其实就是一个类,在请求到来和结束后, django会根据自己的规则在合适的时机执行中间件中相应的方法. 在dj ...

  8. Python-Mongodb vs mysql

    mongodb https://www.cnblogs.com/kermitjam/articles/10147254.html#_label5 centos安装mongodb: https://bl ...

  9. oracle 查询数据库的约束条件

    1.查找表的所有索引(包括索引名,类型,构成列): select t.*,i.index_type from user_ind_columns t,user_indexes i where t.ind ...

  10. Confluence 6 确定一个生产系统备份方案

    Atlassian 推荐创建一个可选的数据库备份方案: 使用你数据库提供的备份和恢复工具 为了避免数据不完整和备份中断,我们推荐你在备份和恢复 Confluence 数据库的时候关闭 Confluen ...