WPF 获取元素(Visual)相对于屏幕设备的缩放比例,可用于清晰显示图片
原文:WPF 获取元素(Visual)相对于屏幕设备的缩放比例,可用于清晰显示图片
我们知道,在 WPF 中的坐标单位不是屏幕像素单位,所以如果需要知道某个控件的像素尺寸,以便做一些与屏幕像素尺寸相关的操作,就需要经过一些计算(例如得到屏幕的 DPI)。
更繁琐的是,我们的控件可能外面有一些其他的控件做了 RenderTransform 进行了一些缩放,于是了解到屏幕像素单位就更不容易了。
本文将提供一套计算方法,帮助计算某个 WPF 控件相比于屏幕像素尺寸的缩放比例,用于进行屏幕像素级别的渲染控制。
一个 WPF 控件会经历哪些缩放?
如下图,我画了一个屏幕,屏幕里面有一个 WPF 窗口,WPF 窗口里面有一个或者多个 ViewBox 或者设置了 RenderTransform 这样的缩放的控件,一层层嵌套下有我们的最终控件。

于是,我们的控件如何得知此时相比于屏幕像素的缩放比呢?换句话说,如何得知此时此控件的显示占了多少个屏幕像素的宽高呢?
分别计算所有的缩放
从上面的图中,我们可以得知,有两种不同种类的缩放:
- 屏幕到 WPF 窗口的缩放
- WPF 窗口内部的缩放
屏幕到 WPF 窗口的缩放
我们知道 WPF 的单位叫做 DIP 设备无关单位。不过,我更希望引入 UWP 中的有效像素单位。实际上 WPF 和 UWP 的像素单位含义是一样的,只是 WPF 使用了一个画饼式的叫法,而 UWP 中的叫法就显得现实得多。
你可以阅读我的另一篇博客了解到有效像素单位:
有效像素主要就是考虑了 DPI 缩放。于是实际上我们就是在计算 DPI 缩放。
// visual 是我们准备找到缩放量的控件。
var ct = PresentationSource.FromVisual(visual)?.CompositionTarget;
var matrix = ct == null ? Matrix.Identity : ct.TransformToDevice;
- 1
- 2
- 3
这里,我们使用的是 PresentationSource.FromVisual(visual)?.CompositionTarget 因为不同屏幕可能存在不同的 DPI。
WPF 窗口内部的缩放
WPF 窗口内部的缩放,肯定不会是一层层自己去叠加。
实际上 WPF 提供了方法 TransformToAncestor 可以计算一个两个具有父子关系的控件的相对变换量。
于是我们需要找到 WPF 窗口中的根元素,可以通过不断查找可视化树的父级来找到根。
// VisualRoot 方法用于查找 visual 当前的可视化树的根,如果 visual 已经显示,则根会是窗口中的根元素。
var root = VisualRoot(visual);
var transform = ((MatrixTransform)visual.TransformToAncestor(root)).Value;
- 1
- 2
- 3
我封装的源码
为了方便使用,我进行了一些封装。
要获取某个 Visual 相比于屏幕的缩放量,则调用 GetScalingRatioToDevice 方法即可。
代码已经上传至 gits:https://gist.github.com/walterlv/6015ea19c9338b9e45ca053b102cf456。
using System;
using System.Windows;
using System.Windows.Media;
namespace Walterlv
{
public static class VisualScalingExtensions
{
/// <summary>
/// 获取一个 <paramref name="visual"/> 在显示设备上的尺寸相对于自身尺寸的缩放比。
/// </summary>
public static Size GetScalingRatioToDevice(this Visual visual)
{
return visual.GetTransformInfoToDevice().size;
}
/// <summary>
/// 获取一个 <paramref name="visual"/> 在显示设备上的尺寸相对于自身尺寸的缩放比和旋转角度(顺时针为正角度)。
/// </summary>
public static (Size size, double angle) GetTransformInfoToDevice(this Visual visual)
{
if (visual == null) throw new ArgumentNullException(nameof(visual));
// 计算此 Visual 在 WPF 窗口内部的缩放(含 ScaleTransform 等)。
var root = VisualRoot(visual);
var transform = ((MatrixTransform)visual.TransformToAncestor(root)).Value;
// 计算此 WPF 窗口相比于设备的外部缩放(含 DPI 缩放等)。
var ct = PresentationSource.FromVisual(visual)?.CompositionTarget;
if (ct != null)
{
transform.Append(ct.TransformToDevice);
}
// 如果元素有旋转,则计算旋转分量。
var unitVector = new Vector(1, 0);
var vector = transform.Transform(unitVector);
var angle = Vector.AngleBetween(unitVector, vector);
transform.Rotate(-angle);
// 计算考虑了旋转的综合缩放比。
var rect = new Rect(new Size(1, 1));
rect.Transform(transform);
return (rect.Size, angle);
}
/// <summary>
/// 寻找一个 <see cref="Visual"/> 连接着的视觉树的根。
/// 通常,如果这个 <see cref="Visual"/> 显示在窗口中,则根为 <see cref="Window"/>;
/// 不过,如果此 <see cref="Visual"/> 没有显示出来,则根为某一个包含它的 <see cref="Visual"/>。
/// 如果此 <see cref="Visual"/> 未连接到任何其它 <see cref="Visual"/>,则根为它自身。
/// </summary>
private static Visual VisualRoot(Visual visual)
{
if (visual == null) throw new ArgumentNullException(nameof(visual));
var root = visual;
var parent = VisualTreeHelper.GetParent(visual);
while (parent != null)
{
if (parent is Visual r)
{
root = r;
}
parent = VisualTreeHelper.GetParent(parent);
}
return root;
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
我的博客会首发于 https://blog.walterlv.com/,而 CSDN 会从其中精选发布,但是一旦发布了就很少更新。
如果在博客看到有任何不懂的内容,欢迎交流。我搭建了 dotnet 职业技术学院 欢迎大家加入。

本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。欢迎转载、使用、重新发布,但务必保留文章署名吕毅(包含链接:https://walterlv.blog.csdn.net/),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。如有任何疑问,请与我联系。
WPF 获取元素(Visual)相对于屏幕设备的缩放比例,可用于清晰显示图片的更多相关文章
- 获取元素top值,屏幕滚动到当前元素
var top = $(this).offset().top; $('html , body').animate({scrollTop: top-100},10);
- jquery获取元素到屏幕底的可视距离
jquery获取元素到屏幕底的可视距离 要打对号的图里的height(我自称为可视高度:滚动条未滑到最底端) 不是打叉图里的到页面底部(滚动条到最底部时的height)(offset().top方法 ...
- js获取元素到屏幕左上角的距离
开发过程中经常会遇到 获取元素到屏幕左上角的距离, 当我们使用jQuery开发时,我们可以使用 $.offset()来获取准确的距离. 如果我们的项目中并没有引入jQuer的话,跟希望通过原生方法实现 ...
- WPF 获取鼠标屏幕位置、窗口位置、控件位置
原文:WPF 获取鼠标屏幕位置.窗口位置.控件位置 public struct POINT { public int X; public int Y; public POINT(int x, int ...
- [WPF]获取鼠标指针下的元素
原文:[WPF]获取鼠标指针下的元素 [WPF]获取鼠标指针下的元素 周银辉 以前写过一些GetElementUnderMouse之类的函数,要用到坐标换算而显得有些麻烦(特别是当元素有XXXTr ...
- 关于js获取元素在屏幕中的位置的方法
针对我们获取元素在页面中的位置的问题,我们还是用老师一峰老师的方法来解决吧 下面上HTML代码 <div class="left_footer"> <p data ...
- WPF中元素拖拽的两个实例
今天结合之前做过的一些拖拽的例子来对这个方面进行一些总结,这里主要用两个例子来说明在WPF中如何使用拖拽进行操作,元素拖拽是一个常见的操作,第一个拖拽的例子是将ListBox中的子元素拖拽到ListV ...
- WPFの获取任意元素的位置
如果布局在Grid中: 方法一: //_stackPanel为子元素,_grid为父元素 Point point = _stackPanel.TranslatePoint(new Point(0, 0 ...
- [WPF]获取控件间的相对位置
原文:[WPF]获取控件间的相对位置 [WPF]获取控件间的相对位置 周银辉 我们知道WPF有着比较灵活的布局方式,关于某个控件的坐标,Canv ...
随机推荐
- mac 使用tesseract识别图片中的中文
安装 tesseractbrew install tesseract 加入环境变量export TESSDATA_PREFIX=/usr/local/Cellar/tesseract/4.1.0/sh ...
- GuavaCache简介(一)是轻量级的框架 少量数据,并且 过期时间相同 可以用 GuavaCache
还有一篇文章是讲解redis 如何删除过期数据的,参考:Redis的内存回收策略和内存上限(阿里) 划重点:在GuavaCache中,并不存在任何线程!它实现机制是在写操作时顺带做少量的维护工作(如清 ...
- 20175211 《实验三 敏捷开发与XP实践》实验报告
目录 一.实验内容 二.实验步骤 四.实验过程中遇到的问题及其解决方法 五.心得体会 六.码云链接 七.结对成员链接 八.参考资料 一.实验内容 (1)编码标准 (2)Git的使用 (3)重构 (4) ...
- vue中select设置默认选中
vue中select设置默认选中 一.总结 一句话总结: 通过v-model来:select上v-model的值为option默认选中的那项的值(value) 二.select设置默认选中实例 < ...
- [转]js判断数据类型的四种方法
原文地址:https://www.cnblogs.com/crackedlove/p/10331317.html 1.typeof typeof是一个操作符,其右侧跟一个一元表达式,并返回这个表达式的 ...
- Supervisor进程守护工具的使用
Supervisor是一个 Python 开发的 client/server 系统,可以管理和监控类 UNIX 操作系统上面的进程. 官网:http://supervisord.org/ G ...
- java判断是excel2003还是2007以上
public static Workbook create(InputStream in) throws IOException,InvalidFormatException { if (!in.ma ...
- java面试题实战二
1.spring 是如何创建bean的? 在IoC容器中,bean的获取主要通过BeanFactory和ApplicationContext获取,这里ApplicationContext实际上是继承自 ...
- [整理]如何撤销远程的git提交?
确保你在你想要撤销的分支上. 第一步,本地使用 get reset --hard ,切换到特定的commit. 第二部,使用 --force推送到远程分支. git reset --hard cedc ...
- vue-cli3用图形化的方式创建项目
Vue脚手架可以快速生成Vue项目基础的架构. A.安装3.x版本的Vue脚手架: npm install -g @vue/cli B.基于3.x版本的脚手架创建Vue项目: 1).使用命令创建Vue ...
