前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >WPF 获取元素(Visual)相对于屏幕设备的缩放比例,可用于清晰显示图片

WPF 获取元素(Visual)相对于屏幕设备的缩放比例,可用于清晰显示图片

作者头像
walterlv
发布2023-10-22 10:05:56
5390
发布2023-10-22 10:05:56
举报
文章被收录于专栏:walterlv - 吕毅的博客

我们知道,在 WPF 中的坐标单位不是屏幕像素单位,所以如果需要知道某个控件的像素尺寸,以便做一些与屏幕像素尺寸相关的操作,就需要经过一些计算(例如得到屏幕的 DPI)。

更繁琐的是,我们的控件可能外面有一些其他的控件做了 RenderTransform 进行了一些缩放,于是了解到屏幕像素单位就更不容易了。

本文将提供一套计算方法,帮助计算某个 WPF 控件相比于屏幕像素尺寸的缩放比例,用于进行屏幕像素级别的渲染控制。


一个 WPF 控件会经历哪些缩放?

如下图,我画了一个屏幕,屏幕里面有一个 WPF 窗口,WPF 窗口里面有一个或者多个 ViewBox 或者设置了 RenderTransform 这样的缩放的控件,一层层嵌套下有我们的最终控件。

这些缩放
这些缩放

于是,我们的控件如何得知此时相比于屏幕像素的缩放比呢?换句话说,如何得知此时此控件的显示占了多少个屏幕像素的宽高呢?

分别计算所有的缩放

从上面的图中,我们可以得知,有两种不同种类的缩放:

  1. 屏幕到 WPF 窗口的缩放
  2. WPF 窗口内部的缩放

屏幕到 WPF 窗口的缩放

我们知道 WPF 的单位叫做 DIP 设备无关单位。不过,我更希望引入 UWP 中的有效像素单位。实际上 WPF 和 UWP 的像素单位含义是一样的,只是 WPF 使用了一个画饼式的叫法,而 UWP 中的叫法就显得现实得多。

你可以阅读我的另一篇博客了解到有效像素单位:

有效像素主要就是考虑了 DPI 缩放。于是实际上我们就是在计算 DPI 缩放。

1 2 3

// visual 是我们准备找到缩放量的控件。 var ct = PresentationSource.FromVisual(visual)?.CompositionTarget; var matrix = ct == null ? Matrix.Identity : ct.TransformToDevice;

这里,我们使用的是 PresentationSource.FromVisual(visual)?.CompositionTarget 因为不同屏幕可能存在不同的 DPI。

WPF 窗口内部的缩放

WPF 窗口内部的缩放,肯定不会是一层层自己去叠加。

实际上 WPF 提供了方法 TransformToAncestor 可以计算一个两个具有父子关系的控件的相对变换量。

于是我们需要找到 WPF 窗口中的根元素,可以通过不断查找可视化树的父级来找到根。

1 2 3

// VisualRoot 方法用于查找 visual 当前的可视化树的根,如果 visual 已经显示,则根会是窗口中的根元素。 var root = VisualRoot(visual); var transform = ((MatrixTransform)visual.TransformToAncestor(root)).Value;

我封装的源码

为了方便使用,我进行了一些封装。

要获取某个 Visual 相比于屏幕的缩放量,则调用 GetScalingRatioToDevice 方法即可。

代码已经上传至 gits:https://gist.github.com/walterlv/6015ea19c9338b9e45ca053b102cf456

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

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; } } }

本文会经常更新,请阅读原文: https://blog.walterlv.com/post/get-wpf-visual-scaling-ratio-to-device.html ,以避免陈旧错误知识的误导,同时有更好的阅读体验。

本作品采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可。欢迎转载、使用、重新发布,但务必保留文章署名 吕毅 (包含链接: https://blog.walterlv.com ),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。如有任何疑问,请 与我联系 ([email protected])

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2019-04-25,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一个 WPF 控件会经历哪些缩放?
  • 分别计算所有的缩放
    • 屏幕到 WPF 窗口的缩放
      • WPF 窗口内部的缩放
      • 我封装的源码
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档