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 ...
随机推荐
- TDD(测试驱动开发)
什么是 TDDTDD 有广义和狭义之分,常说的是狭义的 TDD,也就是 UTDD(Unit Test Driven Development).广义的 TDD 是 ATDD(Acceptance Tes ...
- Linux上tomcat部署web项目,访问报错"ip访问时间过长"
项目原来是可以访问的,后来突然不能不访问了,系统ip也是能ping通的,后来就想是不是防火墙的问题,查看一下还真是 原因:Linux服务器上的防火墙开着,关闭即可 1.查看firewall服务状态 s ...
- HttpClient 连接泄漏问题
AbstractConnPool.java 提交记录 https://github.com/apache/httpcomponents-core/commits/4.4.x/httpcore/src/ ...
- jQuery的zTree插件
写在前面 jQuery的 zTree插件 关键代码 <%@ page language="java" contentType="text/html; charset ...
- dd命令笔记
dd命令 用指定大小的块拷贝一个文件,并在拷贝的同时进行指定的转换 参数 if=FILE 指定输入源文件, 缺省为标准输入, < if=input file > iflag=FLAGS 指 ...
- MQTT研究之EMQ:【EMQX使用中的一些问题记录(1)】
issue 1. EMQX的共享订阅 EMQX是一个非常强大的物联网通信消息总线,基于EMQX开展应用开发,要注意很多配置细节问题,这里要说到的就是共享订阅以及和cleanSession之间的关系问题 ...
- IDEA中Lombok插件的安装及使用
这个插件的好处在于可以让我们的代码更简洁,减少一些重复的工作,最常用的就是@Data注解,比如在实体类上使用@Data注解,实体类的各个属性就不需要书写get和set方法. 安装方法: 1.File→ ...
- NuxtJS实战,一个博客系统
前言 这个项目诞生于17年5月,距今已有两年多了,在这两年期间经历了很多变更,从简单到复杂,又从复杂到简单,并且以后一直会保持这种简单状态.最近迎来了一次更新,因此特意分享一下.虽然只有我一个人使用( ...
- Spring Boot JDBC:加载DataSource过程的源码分析及yml中DataSource的配置
装载至:https://www.cnblogs.com/storml/p/8611388.html Spring Boot实现了自动加载DataSource及相关配置.当然,使用时加上@EnableA ...
- 使用极光第三方IM的时候服务器报错Caused by: java.net.UnknownHostException: api.im.jpush.cn
Caused by: java.net.UnknownHostException: api.im.jpush.cn 服务器报这个:首先查看服务器是否能ping通,如果能ping通,则看下 vi /et ...