精通
英语
和
开源
,
擅长
开发
与
培训
,
胸怀四海
第一信赖
服务方向
联系方式
4K屏幕因为分辨率高,显示结果的区域太小,进行编程或长时间操作会很累,常见的方法是维持分辨率调整屏幕缩放比例,或者调低分辨率,一般选用前者,但是在屏幕缩放下,绘制的处理会由系统接管,位置和大小往往不受自己代码控制,就带来些麻烦。
最近开发的C#窗体WinForm应用程序就遇到这个问题,从网上查到WPF和UWP都可以解决此类问题,但是C#写的代码已经平台化了,短时间内也不想换技术平台,所以就耐心去研究解决办法。
在说解决办法前,介绍一些概念。
通过清单文件,可以添加DPI感知,DPI感知就是让软件的界面由系统接管给你模拟放大下,这样就会模糊,所以要支持高分辨率,其实是要禁止DPI感知,自己做定位和字体大小处理。网上对这个描述很混乱,我也是做了实验才明白。
禁止DPI感知,需要做2个操作,删除清单文件和app.config里的内容,用兼容方式或系统策略或注册表指定软件不用系统DPI支持。
app.config里类似内容:
<add key="DpiAwareness" value="PerMonitorV2" />
<add key="EnableWindowsFormsHighDpiAutoResizing" value="true" />
清单文件里的类似内容:
<application xmlns="urn:schemas-microsoft-com:asm.v3">
<windowsSettings>
<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2</dpiAwareness>
<dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">True</dpiAware>
</windowsSettings>
</application>
上面说的内容要删除,有些网上说的显示模糊就加上面的内容,理解有误,让系统代管才会出问题。
兼容方式如下图:
注册表方式是: HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\AppCompatFlags\Layers
字符串键:“应用程序exe的路径”
值: ~ HIGHDPIAWARE
请注意,“波浪号”和“ HIGHDPIAWARE”之间存在一个空格。
计算机策略方式注意:从左侧依次展开计算机配置-管理模板-Windows组件-应用程序兼容性;
这是WinForm里的一个属性,说是控制缩放,可以按DPI或Font来缩放,其实修改了也看不到效果,我改了几次截图发现窗体大小还哪样。感觉缩放不缩放,还是上面章节说的环境决定的,软件决定不了。
其实在高分辨率下,调大字体,让窗体用Font来指导缩放,自己适配只修改下窗体大小,也是个省力好方法,但是我没调出来这效果,大家也可以试下,互相交流。
去掉DPI感知,就相当于有个干净的环境,但自己的代码要费心组织布局了。因为4K屏幕有复杂性,实际操作的东西太多了,这里开源一个我处理的核心函数的代码,如果对其它的细节遇到问题,欢迎联系锐英源进行技术合作。
public static void ChangeUIFontByDPI(Form frm)
{
frm.SuspendLayout();
if(bNonZoomsys && frm.WindowState==FormWindowState.Normal)//非缩放状态,要自己定义窗口和控件大小
{
frm.Width = (int)(frm.Width*dpixRatio);
frm.Height = (int)(frm.Height* dpiyRatio);
}
foreach (Control ctrl in frm.Controls)
{
if (ctrl is Panel) ChangeUIFontByDPI((Panel)ctrl);
else
{
float fsize = (float)(ctrl.Font.Size * (float)dpixRatio);
if (dpixRatio == 0)
fsize = ctrl.Font.Size;
ctrl.Font = new Font(ctrl.Font.Name, fsize, ctrl.Font.Style);
if (bNonZoomsys)//非缩放状态,要自己定义窗口和控件大小
{
if(ctrl.Anchor!=AnchorStyles.None)
ctrl.Bounds = new Rectangle((int)(ctrl.Location.X ), (int)(ctrl.Location.Y ),
(int)(ctrl.Width * dpixRatio), (int)(ctrl.Height * dpiyRatio));
else
ctrl.Bounds = new Rectangle((int)(ctrl.Location.X * dpixRatio), (int)(ctrl.Location.Y * dpiyRatio),
(int)(ctrl.Width * dpixRatio), (int)(ctrl.Height * dpiyRatio));
}
}
}
frm.ResumeLayout();
}