如何让窗体上绘制的图形不受显示器分辨率/缩放比例变化影响
高DPI缩放环境下多边形绘制偏移问题解决方案
问题背景
基于Jimi的多边形绘制方案开发的WinForms应用,在1920x1200分辨率、100%缩放的设备上显示正常,但切换到2160x1440(推测输入笔误)、150%缩放的设备时,图形的大小和位置出现明显偏移。
解决思路与步骤
一、项目环境设置调整
- 启用每显示器DPI感知:
打开VS2022项目属性→应用程序→窗口设置,将DPI感知设置为每显示器高DPI感知(Per Monitor v2)。
也可以直接修改app.manifest文件,添加以下配置:<application xmlns="urn:schemas-microsoft-com:asm.v3"> <windowsSettings> <dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true/PM</dpiAware> <dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2, PerMonitor</dpiAwareness> </windowsSettings> </application> - 禁用窗体自动缩放:
在窗体属性中,将AutoScaleMode设置为None,避免WinForms自带的缩放逻辑干扰自定义绘制。
二、代码适配修改
核心是针对当前显示器的DPI缩放系数,对所有绘制参数(坐标、半径、画笔宽度)进行比例转换,确保在不同缩放比例下显示效果一致。修改后的完整代码如下:
Public Class Form1 ' 基础参数(基于96DPI/100%缩放设计) Private ReadOnly baseCenter As PointF = New PointF(200, 100) Private ReadOnly baseRadius As Single = 70 Private ReadOnly basePenWidth As Single = 2 Dim numberOfSides As Integer = 0 Const skew As Single = Math.PI * 1.5F ' 修正旋转角度 ' 当前显示器的DPI缩放系数 Private currentDpiScaleX As Single = 1.0F Private currentDpiScaleY As Single = 1.0F Protected Overrides Sub OnLoad(e As EventArgs) MyBase.OnLoad(e) ' 初始化DPI缩放系数 Using g As Graphics = Me.CreateGraphics() currentDpiScaleX = g.DpiX / 96.0F currentDpiScaleY = g.DpiY / 96.0F End Using ' 禁用自动缩放 Me.AutoScaleMode = AutoScaleMode.None End Sub Protected Overrides Sub OnPaint(e As PaintEventArgs) MyBase.OnPaint(e) If numberOfSides < 3 Then Return ' 计算缩放后的绘制参数 Dim scaledCenter As PointF = New PointF(baseCenter.X * currentDpiScaleX, baseCenter.Y * currentDpiScaleY) Dim scaledRadius As Single = baseRadius * currentDpiScaleX Dim scaledPenWidth As Single = basePenWidth * currentDpiScaleX Dim polygon = CreatePolygon(scaledRadius, numberOfSides, scaledCenter) Using blackPen As New Pen(Color.Black, scaledPenWidth) e.Graphics.SmoothingMode = Drawing2D.SmoothingMode.AntiAlias e.Graphics.DrawPolygon(blackPen, polygon) End Using End Sub Private Sub ListBox1_SelectedIndexChanged(sender As Object, e As EventArgs) Handles ListBox1.SelectedIndexChanged numberOfSides = CInt(ListBox1.SelectedItem) Me.Invalidate() End Sub Public Function CreatePolygon(radius As Single, sides As Integer, center As PointF) As PointF() Dim polygon = New PointF(sides - 1) {} Dim angle As Single = 360.0F / sides * CSng(Math.PI / 180.0F) For side As Integer = 0 To sides - 1 polygon(side) = New PointF( CSng(center.X + radius * Math.Cos(skew + side * angle)), CSng(center.Y + radius * Math.Sin(skew + side * angle))) Next Return polygon End Function ' 处理跨显示器DPI变化事件 Protected Overrides Sub OnDpiChanged(e As DpiChangedEventArgs) MyBase.OnDpiChanged(e) currentDpiScaleX = e.DpiNew.X / 96.0F currentDpiScaleY = e.DpiNew.Y / 96.0F Me.Invalidate() End Sub End Class
关键修改说明
- DPI缩放系数计算:以标准96DPI为基准,获取当前显示器的实际DPI值计算缩放比例,保证所有绘制元素按比例放大/缩小。
- 监听DPI变化:当窗体在不同DPI的显示器间切换时,自动更新缩放系数并刷新绘制,保证显示一致性。
- 禁用自动缩放:完全接管绘制逻辑,避免WinForms自动缩放导致的偏移。
内容的提问来源于stack exchange,提问作者Florin Home
相关产品推荐
相关产品推荐

