VB.NET VSTO Excel插件窗体移至副屏后调整尺寸异常问题咨询
问题根因
该问题由多屏不同缩放比例场景下Windows Forms的DPI感知配置缺失,以及VSTO宿主的默认DPI适配逻辑冲突导致,2.5倍的放大系数正好匹配主屏的250%缩放设置:未开启逐屏DPI感知时,窗体始终继承主屏的缩放逻辑,移到100%缩放的副屏后设置尺寸时,系统会自动对输入的尺寸再叠加一次主屏缩放比例转换,最终导致实际尺寸超出预期。你看到的Me.DeviceDpi始终为96,正是因为窗体默认未开启逐屏DPI感知,系统统一返回96DPI后自行拉伸窗体导致。
解决方案
- 第一步:升级项目目标框架到.NET Framework 4.7.2及以上版本,获得稳定的逐屏DPI感知支持。
- 第二步:修改项目的
app.config配置,开启PerMonitorV2 DPI感知:
<configuration> <runtime> <AppContextSwitchOverrides value="Switch.System.Windows.Forms.DoNotPerformDpiScalingInDialogHost=false" /> </runtime> <System.Windows.Forms.ApplicationConfigurationSection> <add key="DpiAwareness" value="PerMonitorV2" /> </System.Windows.Forms.ApplicationConfigurationSection> </configuration>
- 第三步:在窗体代码中引入Win32 API用于获取屏幕实际缩放系数,添加如下声明:
Imports System.Runtime.InteropServices Public Class YourFormClass <DllImport("gdi32.dll")> Private Shared Function GetDeviceCaps(ByVal hdc As IntPtr, ByVal nIndex As Integer) As UInteger End Function ' 其余原有代码 End Class
- 第四步:将窗体的
AutoScaleMode属性设置为AutoScaleMode.None,关闭系统默认的自动缩放逻辑,改为手动控制尺寸转换。 - 第五步:添加缩放系数获取方法,修正窗体移动与尺寸设置代码,先根据目标屏幕的缩放系数换算正确的尺寸值:
' 获取指定屏幕的缩放系数 Private Function GetScreenScalingFactor(screen As Screen) As Single Using g As Graphics = Graphics.FromHwnd(IntPtr.Zero) Dim desktopPtr As IntPtr = g.GetHdc() Dim dpiX As UInteger = GetDeviceCaps(desktopPtr, 88) ' 88对应LOGPIXELSX,即水平DPI g.ReleaseHdc(desktopPtr) Return dpiX / 96.0F End Using End Function ' 修正后的移动+调整尺寸代码 Dim activeScreen As Screen = ' 你原有逻辑获取的目标副屏 Dim scalingFactor As Single = GetScreenScalingFactor(activeScreen) Me.StartPosition = FormStartPosition.Manual ' 位置直接取副屏对应坐标即可,无需额外叠加偏移 Me.Location = New Point(activeScreen.Bounds.X + 100, activeScreen.Bounds.Y + 100) ' 按目标屏幕缩放系数换算实际需要设置的尺寸,避免系统自动缩放 Dim targetWidth As Integer = CInt(2278 / scalingFactor) Dim targetHeight As Integer = CInt(308 / scalingFactor) Me.Size = New Size(targetWidth, targetHeight)
验证说明
修改后重新生成插件,再将窗体移到副屏调整尺寸时,系统不会再自动叠加缩放系数,尺寸会和你的预期值一致,且DeviceDpi属性会随窗体所在屏幕的缩放比例自动变化。
内容的提问来源于stack exchange,提问作者Andrew Goldman
相关产品推荐
相关产品推荐

