在很久很久以前,WPF 诞生之初,有一个神奇的单位,它的名字叫做——设备无关单位(DIP,Device Independent Unit)。微软给它描绘了一片美好的愿景——在任何显示器上显示的尺寸是相同的。

What the ** is this unit!!! 神 TM 相同!!!

UWP 采用有效像素(Effective Pixels)来描述尺寸,这是才是能够自圆其说的一套尺寸描述;WPF 的尺寸机制与 UWP 完全就是同一套,使用有效像素才能解释 WPF 尺寸变化上的各种特性!


统一概念

在继续讨论之前,我们必须统一几个概念。不能说那些意义不明确的词,尤其是“宽高”“大小”“尺寸”“更大”。试想你说一个按钮的宽高是 200,那么它的宽高到底是多少呢?一个屏幕上的按钮和另一个屏幕上的按钮哪个更大呢?

在本文中,对于尺寸,我们只说三个概念:

  • 物理尺寸(单位:厘米)
  • 显示器像素个数(单位:个)
  • 有效像素(即 WPF 中最常用的那个单位;在本文结束之前,这应该是一个未定义的概念)

如果我们说 A 按钮比 B 按钮的物理宽度更大,那么无论它们显示在哪个显示器上,都具备相同的关系——因为我们可以拿尺子来量。

如果我们说 A 按钮比 B 按钮在宽度上占用的显示器像素个数更多,我们也可以拿放大镜去屏幕上一个点一个点地数——当然,各种截图工具已经在最佳分辨率下具备数像素个数的功能了(这里一定要突出最佳分辨率)。

而有效像素(Effective Pixels,本文记其为 epx)就是本文从 UWP 中引入的尺寸概念。当我们说按钮的有效像素宽度为 200 时,指的是你在 WPF 的 XAML 或 C# 代码中写下了 Width="200"

接下来,当我们谈论尺寸时,只会用以上三个概念进行比较,而不会再用模糊不清的尺寸名词。


从愿景看有效像素的意义

有效像素单位的诞生一定是为了解决某种尺寸问题,而且是因为现有的尺寸单位无法简单地描述这一问题。而我们就要准确描述这一问题,并将得到的单位定义成“有效像素”。

吐槽 DIP

WPF 曾经说自己用的是“设备无关单位”(DIP),愿景是在所有显示器上显示的物理尺寸相同。比如你在代码中写了 Width="96" 的按钮,那么在所有显示器上其尺寸为 1 英寸。

其实简单测试就不难发现这是一个根本无法自圆其说的愿景,具体无法自圆其说的点有两个。

  1. WPF 说自己的开发无需考虑 DPI 缩放,因为它会自己做缩放。那么当你写下 Width="96" 时,到底缩放还是不缩放呢?缩放就迁就了 DPI 缩放的特性,违背了物理尺寸相同的特性;不缩放就迁就了物理尺寸相同的特性,丢失了 DPI 缩放的特性。
  2. 在非 PC 设备(手机、平板电脑、大屏幕电视)上,如果依然保持物理尺寸相同,那么 PC 上显示合适的 3cm 的按钮在手机上将占据大半个屏幕,在电视上将小得几乎看不见。怎么能让一个 UI 框架做出这么脑残的设计呢?

有效像素(epx)的愿景

有效像素概念的出现,就摒除了 WPF 物理尺寸相同这样荒谬而无法自圆其说的设定。但为了给有效像素设下定义,我们来看看微软到底期望这样的尺寸单位带来哪些方便吧:

▲ 如果此处看不到视频,请前往 Channel 9 观看:Designing Universal Windows Platform apps

具体说来,对于手机和平板电脑(笔记本、Surface)这些近距离观看的设备,其物理尺寸可以更小;对于客厅摆放的大屏幕电视,观看距离较远,物理尺寸应该更大。相同的界面元素在不同设备上显示时,呈现出来的效果在视野角度上是相近的,这才是人眼观看比较舒适的尺寸概念的设计。

视野角度相近
▲ 图片来自于微软 UWP 设计指导文档 Introduction to Universal Windows Platform (UWP) app design (Windows apps)

可以看出,有效像素的出现解决了我在 以上吐槽 中列举出无法自圆其说的第 2 点。认识到一个优秀的屏幕显示单位并不是按物理尺寸定义,而是根据不同的使用场景有所不同。第 1 点也部分得到了缓解——接受 DPI 缩放的特性,放弃承认物理尺寸相同的设定。

有效像素(epx)的局限性

一个好的概念除了要充分展示自己的愿景,也要看清自己的局限性。

而有效像素的局限性就在于——它的愿景只是理想状态下才能有的效果,而微软本身允许硬件厂商和用户进行设置以偏离理想状态。

具体是这样的:

  1. 显示器厂商为了自己的销售目标,会推出各种各样的型号——有高端的,有低端的,有主打性价比的。而性价比的主要来源就是——“噱头”——在部分参数上非常漂亮,部分参数却缩水严重。比如同样是 34 寸的 21:9 超宽屏显示器,5999 元的可以是 3440×1440 像素个数,而 3999 元的就只能是 2560×1080 像素个数。前者每英寸像素点数接近 96,而后者则低多了。Windows 操作系统上支持的最低 DPI 设置只能是 96 了,不能再低;以至于后者实际上在相同观看距离上比前者显示的界面元素的物理尺寸会大很多。这是硬件厂商的销售策略,你一个虚拟的单位还能拿它怎么样!
    这其实是此愿景不能实现的最主要原因了——各大显示器厂商都存在不按照最佳观看效果设置显示器参数的问题。这也是为什么我们经常能发现有些笔记本上的图标和字体大小被默认设置得小得可怜,或者超大屏幕设备上文字小得远处看不清的原因。(这里我们只谈显示器厂商在 EDID 信息中设置的最佳时序,这个时序欺骗了操作系统使之给出了不合适的显示效果;不要说用户可以改的问题,毕竟让用户改已经提高了门槛了。)

  2. 用户可以随时修改屏幕显示分辨率,修改系统或屏幕的 DPI 值。在显示分辨率与显示器实际物理分辨率不一致的情况下,用户还能设置画面的填充方式(居中或是拉伸)。
    我们认为,用户主动设置 DPI 是出于自己的一些目的——希望放大或缩小界面元素,而这有可能是因为原有大小对用户自己来说看起来不够舒适。(这里不想吐槽设置分辨率还不设置为居中显示的用户,那种画面模糊的感觉,怎么能承受!)

事实上,目前为止,只有一款设备真正达到了微软期望中的理想状态,那就是——Surface Studio!其它都只能算作接近,或者连接近都算不上。

给有效像素下个定义

结合微软对有效像素的愿景,结合实际情况,我认为“有效像素”的定义应该是这样的:

在理想状态下,1 有效像素等于用户观看距离 50cm 时,观看屏幕上 1/96 英寸的物理距离所对应的视角大小
非理想状态下,1 有效像素等于显示器点对点显示像素时,1 屏幕像素乘以系统 DPI 值除以 96

WPF 和 UWP 的尺寸单位都可以用有效像素来理解,而这本身就是它们两个框架内建的单位系统。(彻底抛弃那个不能自圆其说的 DIP 吧!)

有效像素的特性

在以上定义之下,再研究有效像素的特性时,我们便能接受那些非理想状态下的不同行为,不再像 WPF 的 DIP 那样绝对而富有争议。

按钮的大小之争

当我们在代码中写下 Width="96" 时,这个按钮到底多大?

谈物理尺寸:

  • 在 Surface Studio 这样的理想设备上,如果用户没有胡乱设置,它的物理宽度是 1 英寸;
  • 在同一个显示器设备上,如果显示器的 PPI 是 96 pixels/inch,且用户使用最佳分辨率
    • DPI 值设置为 96,则它的物理宽度是 1 英寸
    • DPI 值设置为 144,则它的物理宽度是 1.5 英寸
    • DPI 值设置为 192,则它的物理宽度是 2 英寸
  • 在以上情况下,如果 DPI 值固定为 96,但用户降低了分辨率
    • 居中点对点显示,则它的物理宽度是 1 英寸
    • 拉伸显示,则它的物理宽度大于 1 英寸
  • 换一台显示器,PPI 值更大,则相同情况下的每一种情况都比以上物理宽度更小

谈显示器像素个数:

  • 用户使用了最佳分辨率
    • 在 DPI 值为 96 时,显示完按钮宽度所用的屏幕像素个数为 96
    • DPI 值设置为 192 时,则显示完按钮宽度所用的屏幕像素个数是 192
  • 在以上情况下,如果用户降低了分辨率
    • 居中点对点显示,显示完按钮宽度所用的屏幕像素个数为 96
    • 拉伸显示,显示完按钮宽度所用的屏幕像素个数大于为 96,虚拟的系统像素个数依然等于 96

接受现实

看看按钮实际的大小,你会发现,影响因素真的太多太多了!当我们不再沉浸在 DIP 的理想中,不再纠结有效像素的愿景的时候,便能觉得有效像素其实为我们考虑 DPI 缩放问题做了不少努力,确实能减轻我们 UI 的开发工作量。


本文会经常更新,请阅读原文: https://walterlv.github.io/post/introduce-uwp-effective-pixels-into-wpf.html ,以避免陈旧错误知识的误导,同时有更好的阅读体验。

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