关于WinForms的跨显示器DPI自适应
點(diǎn)擊上方藍(lán)字關(guān)注“汪宇杰博客”
導(dǎo)語
WinForms 是運(yùn)行在Windows上的傳統(tǒng).NET桌面應(yīng)用技術(shù)框架。由于歷史原因,它對(duì)高DPI以及跨不同DPI屏幕的支持有些問題,本文將探索盡可能的解決方案。
Windows 的“黑歷史”
Windows 系統(tǒng)的默認(rèn)DPI(更確切的說法叫 PPI)是96。PPI 的意思是 Pixels per inch,也就是每英寸屏幕顯示多少像素的意思。這個(gè)值越高,表示屏幕的顯示能力越細(xì)膩。
但也意味著,要顯示物理尺寸和低PPI屏幕相同的畫面,高PPI屏幕需要更多的像素來填充。對(duì)于一張非矢量圖來說,這個(gè)問題不好解決,由于向高像素拉伸,這個(gè)圖片會(huì)被“拉模糊”。類似的問題也發(fā)生在Windows中,特別是老程序,設(shè)計(jì)的時(shí)候只考慮了96 PPI。而這個(gè)歷史原因可以在微軟的這篇博客里找到詳解:
https://blogs.msdn.microsoft.com/fontblog/2005/11/08/where-does-96-dpi-come-from-in-windows/
例:在150% DPI的屏幕上,Windows管理控制臺(tái)(MMC)均會(huì)發(fā)生模糊。(可能在微信或網(wǎng)頁里不明顯)
而在100% DPI 的屏幕上,圖像是清晰的。
努力的 Windows 10
由于現(xiàn)在的電腦高分屏(HDPI)越來越多,Windows 10 每半年一次的 Feature Update 一直在努力解決 DPI 的問題。我們可以通過下圖的設(shè)置搭配,解決很多老程序的DPI適配。但是很難做到跨屏幕DPI自適應(yīng)。
所謂跨屏幕DPI自適應(yīng)(Per Monitor-DPI aware),意思就是當(dāng)你的電腦有外接屏幕時(shí),Windows會(huì)選擇適配該屏幕的DPI來顯示外接屏幕的圖像。這個(gè)DPI很可能和你電腦的主屏幕是不一樣的。例如,用 Surface Pro 外接一個(gè) 1920x1080 的22寸顯示器,那么Surface的主屏幕通常是 150%以上DPI,而外接顯示器是100%。
如果程序自己不支持 Per Monitor-DPI aware,那么你用Windows自帶的兼容模式調(diào)整完,會(huì)發(fā)現(xiàn)雖然兩個(gè)屏幕都是清晰的圖像,但是應(yīng)用界面在低DPI屏幕上會(huì)被放大。并不完美。所以最地道的解決方式,是開發(fā)支持 Per Monitor-DPI aware 的程序。
微軟自己的應(yīng)用也有這方面的改進(jìn)。例如 Visual Studio 2019 開始已經(jīng)天然支持 Per Monitor-DPI aware。(要求 Windows 10 v1803及.NET Framework 4.8)
WinForms 能搶救嗎
Windows桌面開發(fā)最native的三種技術(shù)分別是:WinForms、WPF、UWP。因?yàn)閁WP誕生于現(xiàn)代,所以天生沒有DPI適配問題。而WPF的XAML界面也可以輕松適配DPI。唯獨(dú) WinForms 歷史包袱太重,不改是不行的。我們來試試能否搶救。
首先,我在VS2019中使用150% DPI的主屏幕,設(shè)計(jì)器視圖不模糊,但按鈕尺寸有問題,控件位置如下:
留意紅色箭頭位置。在VS里一切正常。然而運(yùn)行起來,在150% DPI的主屏幕是會(huì)模糊,并且控件錯(cuò)位。
將窗口拖動(dòng)到100% DPI的屏幕上,UI不模糊,但控件依舊錯(cuò)位。
按照微軟官網(wǎng)文檔 https://docs.microsoft.com/en-us/dotnet/framework/winforms/high-dpi-support-in-windows-forms 的描述,.NET Framework 從4.7開始,改善WinForms的DPI支持。因此第一步,我將該程序的運(yùn)行時(shí)改為4.7.2(Windows 10 1803以上版本自帶)
在應(yīng)用根目錄加入一個(gè) app.manifest 文件。
取消注釋其中的 assembly/compatibility/application 下的Windows 10 GUID。
<!-- Windows 10 -->
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />
然后在 App.config 底下加入:
<System.Windows.Forms.ApplicationConfigurationSection>
? <add key="DpiAwareness" value="PerMonitorV2" />
</System.Windows.Forms.ApplicationConfigurationSection>
現(xiàn)在發(fā)現(xiàn)控件位置在150% DPI的主屏幕上正確顯示,整個(gè)UI不模糊。
但是在 100% DPI 的屏幕上,雖然UI不模糊,但是控件位置依然不正確,并且TextBox變的巨大無比。
微軟文檔里沒提別的方法。但是我發(fā)現(xiàn)將運(yùn)行時(shí)改成.NET Framework 4.8 可以修復(fù)這個(gè)TextBox的爆,但是控件位置依然不正確。
經(jīng)過仔細(xì)觀察,發(fā)生問題的并不是 TextBox、Label、Checkbox 這幾個(gè)控件,而是 MonthCalendar 在100% DPI的屏幕上比 150% 的主屏寬。并且 Panel、TableLayoutPanel和Dock的組合拳都沒法辦法解決這個(gè)問題。
.NET Core 3.0 能解決嗎?
.NET Core 3.0 目前還在preview 6階段。從我實(shí)驗(yàn)的結(jié)果來看,它的DPI適配不需要App.config,而是在Program.cs里加入:
Application.SetHighDpiMode(HighDpiMode.PerMonitorV2);
但最終效果和以上的.NET Framework 4.8的效果是一樣的,雖然跨DPI屏幕界面不會(huì)模糊,但是 MonthCalendar 的寬度問題依舊。
結(jié)論
在 Windows 10 v1903 上(其他版本我沒試過),通過 .NET Framework 4.8 + app.manifest + app.config 的配置,可以一定程度上讓 WinForms 具有 Per Monitor-DPI aware 的能力,但是部分控件的尺寸還是會(huì)不一樣,因此發(fā)布程序之前需要仔細(xì)測(cè)試,保證UI可用性,再向用戶提供跨屏幕DPI自適應(yīng)支持。
我的樣例程序代碼:https://github.com/EdiWang/DotNet-Samples/tree/master/WinForms-DPI-PMA/PerMonitorAwareDPIForms
總結(jié)
以上是生活随笔為你收集整理的关于WinForms的跨显示器DPI自适应的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 程序员修神之路--做好分库分表其实很难之
- 下一篇: .NET开发框架(九)-NLB网络负载平