【WPF】使用RenderTargetBitmap截图的时候位置出现偏移的一些解决办法

news/2025/1/15 12:28:01/文章来源:https://www.cnblogs.com/luoyisi/p/18672776

简介

在WPF中,如果你需要把控件的渲染画面保存到图片,那么唯一的选择就是RenderTargetBitmap。
不过,RenderTargetBitmap是个比较难伺候的主,有时候你以为能工作,但实际上不能;你以为能够正常截图,但实际上截出来的图片是歪的。
所以,我总结一下自己项目中遇到的坑和解决办法吧!

保存的图片是黑色的

这种情况下,通常是WPF的RenderTargetBitmap无法正确获取视觉对象的渲染结果,我比较建议在需要截图的控件之上包装一层Border。

例如:

<ux:RarityRibbon Background="{Binding Background, Mode=OneWay, Converter={x:Static wf:Converters.StringToBrush}}"Foreground="{Binding Foreground, Mode=OneWay, Converter={x:Static wf:Converters.StringToBrush}}"RarityStarColor="{Binding Stroke, Mode=OneWay, Converter={x:Static wf:Converters.StringToBrush}}"Title="{Binding Name, Mode=OneWay}"Value="{Binding Value, Mode=OneWay}"Level="{Binding Level, Mode=OneWay}"Margin="0"><ux:RarityRibbon.MaskedBrush><ImageBrush ImageSource="{Binding Image, Mode=OneWay, Converter={x:Static wf:Converters.Icon}}"Stretch="Uniform"Viewbox="0.1 0.1 0.5 0.5"Opacity="0.15" /></ux:RarityRibbon.MaskedBrush></ux:RarityRibbon>

要截图的是ux:RarityRibbon控件,但保存的图片是纯黑的。
这时候,要做的就是在ux:RarityRibbon控件之上,包装一层Border,然后把截图对象改成Border即可。

<Border x:Name="Container"><ux:RarityRibbon Background="{Binding Background, Mode=OneWay, Converter={x:Static wf:Converters.StringToBrush}}"Foreground="{Binding Foreground, Mode=OneWay, Converter={x:Static wf:Converters.StringToBrush}}"RarityStarColor="{Binding Stroke, Mode=OneWay, Converter={x:Static wf:Converters.StringToBrush}}"Title="{Binding Name, Mode=OneWay}"Value="{Binding Value, Mode=OneWay}"Level="{Binding Level, Mode=OneWay}"Margin="0"><ux:RarityRibbon.MaskedBrush><ImageBrush ImageSource="{Binding Image, Mode=OneWay, Converter={x:Static wf:Converters.Icon}}"Stretch="Uniform"Viewbox="0.1 0.1 0.5 0.5"Opacity="0.15" /></ux:RarityRibbon.MaskedBrush></ux:RarityRibbon></Border>

控件位置发生了偏移

  1. 要截图的控件必须保证Margin属性没有赋值
  2. 要截图的控件必须保证父级元素没有使用Grid.ColumnDefnitions或者Grid.RowDefnitions属性,这个强制布局的属性会影响RenderTargetBitmap工作。
  3. 要截图的控件必须不适用HorizontalAlignment以及VerticalAlignment,这两个属性会影响RenderTargetBitmap工作。

控件大小无法对应

可以试试Snoop里面截图的帮助类,还挺有用的。


// (c) Copyright Cory Plotts.
// This source is subject to the Microsoft Public License (Ms-PL).
// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
// All other rights reserved.namespace Snoop.Infrastructure;using System;
using System.IO;
using System.Reflection;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Interop;
using System.Windows.Media;
using System.Windows.Media.Imaging;public static class VisualCaptureUtil
{private const double BaseDpi = 96;public static void SaveVisual(Visual visual, int dpi, string filename){// sometimes RenderTargetBitmap doesn't render the Visual or doesn't render the Visual properly// below i am using the trick that jamie rodriguez posted on his blog// where he wraps the Visual inside of a VisualBrush and then renders it.// http://blogs.msdn.com/b/jaimer/archive/2009/07/03/rendertargetbitmap-tips.aspxvar visualBrush = CreateVisualBrushSafe(visual);if (visual is null|| visualBrush is null){return;}var renderTargetBitmap = RenderVisualWithHighQuality(visual, dpi, dpi);SaveAsPng(renderTargetBitmap, filename);}public static RenderTargetBitmap SaveVisual(Visual visual, int dpi){// sometimes RenderTargetBitmap doesn't render the Visual or doesn't render the Visual properly// below i am using the trick that jamie rodriguez posted on his blog// where he wraps the Visual inside of a VisualBrush and then renders it.// http://blogs.msdn.com/b/jaimer/archive/2009/07/03/rendertargetbitmap-tips.aspxvar visualBrush = CreateVisualBrushSafe(visual);if (visual is null || visualBrush is null){return null;}return RenderVisualWithHighQuality(visual, dpi, dpi);}public static VisualBrush CreateVisualBrushSafe(Visual visual){return IsSafeToVisualize(visual)? new VisualBrush(visual): null;}public static bool IsSafeToVisualize(Visual visual){if (visual is null){return false;}if (visual is Window){var source = PresentationSource.FromVisual(visual) as HwndSource;return source?.CompositionTarget is not null;}return true;}private static void SaveAsPng(RenderTargetBitmap bitmap, string filename){var pngBitmapEncoder = new PngBitmapEncoder();pngBitmapEncoder.Frames.Add(BitmapFrame.Create(bitmap));using (var fileStream = File.Create(filename)){pngBitmapEncoder.Save(fileStream);}}/// <summary>/// Draws <paramref name="visual"/> in smaller tiles using multiple <see cref="VisualBrush"/>./// </summary>/// <remarks>/// This way we workaround a limitation in <see cref="VisualBrush"/> which causes poor quality for larger visuals./// </remarks>public static RenderTargetBitmap RenderVisualWithHighQuality(Visual visual, int dpiX, int dpiY, PixelFormat? pixelFormat = null, Viewport3D viewport3D = null){var size = GetSize(visual);var drawingVisual = new DrawingVisual();using (var drawingContext = drawingVisual.RenderOpen()){DrawVisualInTiles(visual, drawingContext, size);}return RenderVisual(drawingVisual, size, dpiX, dpiY, pixelFormat, viewport3D);}public static RenderTargetBitmap RenderVisual(Visual visual, Size bounds, int dpiX, int dpiY, PixelFormat? pixelFormat = null, Viewport3D viewport3D = null){var scaleX = dpiX / BaseDpi;var scaleY = dpiY / BaseDpi;pixelFormat ??= PixelFormats.Pbgra32;var renderTargetBitmap = new RenderTargetBitmap((int)Math.Ceiling(scaleX * bounds.Width), (int)Math.Ceiling(scaleY * bounds.Height), dpiX, dpiY, pixelFormat.Value);if (viewport3D is not null){typeof(RenderTargetBitmap).GetMethod("RenderForBitmapEffect", BindingFlags.Instance | BindingFlags.NonPublic)?.Invoke(renderTargetBitmap, new object[] { visual, Matrix.Identity, Rect.Empty });}else{renderTargetBitmap.Render(visual);}return renderTargetBitmap;}private static Size GetSize(Visual visual){if (visual is UIElement uiElement){return uiElement.RenderSize;}var descendantBounds = VisualTreeHelper.GetDescendantBounds(visual);return new Size(descendantBounds.Width, descendantBounds.Height);}/// <summary>/// Draws <paramref name="visual"/> in smaller tiles using multiple <see cref="VisualBrush"/> to <paramref name="drawingContext"/>./// This way we workaround a limitation in <see cref="VisualBrush"/> which causes poor quality for larger visuals./// </summary>/// <param name="visual">The visual to be drawn.</param>/// <param name="drawingContext">The <see cref="DrawingContext"/> to use.</param>/// <param name="visualSize">The size of <paramref name="visual"/>.</param>/// <param name="tileWidth">The width of one tile.</param>/// <param name="tileHeight">The height of one tile.</param>/// <remarks>/// Original version of this method was copied from https://srndolha.wordpress.com/2012/10/16/exported-drawingvisual-quality-when-using-visualbrush/////// A tile size of 32x32 turned out deliver the best quality while not increasing computation time too much./// </remarks>private static void DrawVisualInTiles(Visual visual, DrawingContext drawingContext, Size visualSize, double tileWidth = 32, double tileHeight = 32){var visualWidth = visualSize.Width;var visualHeight = visualSize.Height;var verticalTileCount = visualHeight / tileHeight;var horizontalTileCount = visualWidth / tileWidth;for (var i = 0; i <= verticalTileCount; i++){for (var j = 0; j <= horizontalTileCount; j++){var width = tileWidth;var height = tileHeight;// Check if we would exceed the width of the visual and limit it by the remainingif ((j + 1) * tileWidth > visualWidth){width = visualWidth - (j * tileWidth);}// Check if we would exceed the height of the visual and limit it by the remainingif ((i + 1) * tileHeight > visualHeight){height = visualHeight - (i * tileHeight);}var x = j * tileWidth;var y = i * tileHeight;var rectangle = new Rect(x, y, width, height);var contentBrush = new VisualBrush(visual){Stretch = Stretch.None,AlignmentX = AlignmentX.Left,AlignmentY = AlignmentY.Top,Viewbox = rectangle,ViewboxUnits = BrushMappingMode.Absolute};drawingContext.DrawRectangle(contentBrush, null, rectangle);}}}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/869555.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

大模型备案流程-简易易懂

大模型备案除了资料撰写难度高外,难点还在于各省没有统一标准。备案流程、资料要求、考察重点都会有些许差异。不过,各省的大体申报流程都如下文所示(各省主要差异点我会标出,具体内容可以一起沟通交流): 一、备案申请 报请申请者所在省份/直辖市/自治区网信:向企业注册地…

KingbaseES RAC集群案例之---jmeter压测

KingbaseES RAC、jmeter案例说明: 通过jmeter压测,测试KingbaseES RAC集群负载均衡功能。 数据库版本: test=# select version();version ---------------------KingbaseES V008R006 (1 row)测试架构:一、jmeter版本 1、系统jiava版本 [root@node203 ~]# java -version ope…

{LOJ #6041. 「雅礼集训 2017 Day7」事情的相似度 题解

\(\text{LOJ \#6041. 「雅礼集训 2017 Day7」事情的相似度 题解}\) 解法一 由 parent 树的性质得到,前缀 \(s_i,s_j\) 的最长公共后缀实质上就是 \(i,j\) 在 SAM 中的 \(\operatorname{LCA}\) 在 SAM 中的 \(\operatorname{len}\)。让我们考虑如何处理 \((l,r)\) 区间内的询问…

解决Hyper-V保留端口导致各种端口占用报错的问题

0.有时候在本地启用一个服务比如MySQL服务,或者在启用IDEA的调试的时候,或者在本地启用一个监听端口的时候可能会出现监听失败的情况,经过查找之后会发现并没有应用占用相应的端口。 1.经过查找发现其实是在启用了Hyper-V之后系统会保留一些端口,这些端口如果包含了你应用要…

D. Madoka and The Corruption Scheme -- (贪心,组合数学,构造)

题目链接:Problem - D - Codeforces 题目大意: 一共n轮比赛,有\(2^n\)个参赛者,第\(i\)轮有\(2^{n - i}\) 场比赛,Madoka能安排第一局的比赛,她想让最后的赢家编号更小,主办方最多有k次操作,能修改任意每一场比赛的获胜情况,可以让最终赢家编号更 大,求Madoka在主办方…

PHP语法基础

PHP语法基础php文档拓展名是.phpphp文件通常包含html标签以及一些php脚本运行代码 ,注意:html js css可以在php文件执行但是,php不能在html js css在php文件执行php语法用;结尾 <!DOCTYPE html> <html> <body> <h1>我的第一张php页面><h1>…

本地打包docker images并上传到服务器.250115

情景: 服务器docker Pull 拉不下来 docker pull easzlab/kubeasz-k8s-bin:v1.31.2 Get "https://registry-1.docker.io/v2/": net/http: request canceled while waiting for connection (Client.Timeout exceeded while awaiting headers) 2025-01-14 17:06:35 [ez…

包豪斯学院

包豪斯学院(Bauhaus)是20世纪最具影响力的艺术与设计学府之一,创立于1919年,由建筑师沃尔特格罗皮乌斯(Walter Gropius)在德国魏玛建立。作为现代主义设计的先锋,包豪斯不仅在建筑、工艺、艺术和设计等领域开创了新局面,其设计理念更是深刻影响了全球的艺术与工业生产方…

主机PHP版本过低导致网页无法正常运行的解决办法

问题描述: 用户发现其主机上的PHP版本过低,导致某些功能无法正常使用,影响了网站的整体性能。此外,用户询问是否可以通过升级主机获得免费域名赠品,以及数据库空间不足的问题。 解决方案: 针对您遇到的主机PHP版本过低的问题,这里提供一些解决方案和建议,帮助您顺利升级…

如何解决网站在多台电脑上打开速度慢的问题

问题描述: 用户反馈,其家庭和单位的电脑在访问某个特定域名时速度非常慢,但手机端访问速度正常。此外,用户还提到服务器存在大量漏洞,担心网站安全问题,并询问如何处理这些漏洞。 解决方案: 针对您提到的家庭和单位电脑访问域名速度慢的问题,我们首先需要排查以下几个方…

如何处理宝塔面板升级失败及账户密码重置?

针对您遇到的宝塔面板升级失败以及账户密码重置的问题,我们将为您提供详细的解决方案。 宝塔面板升级失败 宝塔面板升级失败可能是由多种原因引起的,常见的原因包括但不限于以下几点:网络连接不稳定:在升级过程中,如果网络连接中断或者速度过慢,可能会导致下载更新包失败…