You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在.NET Framework环境下触发WinForms的DpiChanged事件?

WinForms DpiChanged 事件无法触发解决方案

根因说明

.NET Framework 4.7+ 版本的 WinForms 内部DpiHelper类存在隐藏的触发条件限制,即使完成manifest和app.config的基础配置,也可能因为内部开关未激活、控件非顶层等原因,导致原生DpiChanged事件被拦截,无法正常触发。

一、基础配置校验

首先确认配置项的位置和格式完全符合要求:

  • app.config 中必须同时配置DpiAwareness和EnableWindowsFormsHighDpiAutoResizing,仅添加后者无效,配置节点如下:
<configuration>
  <System.Windows.Forms.ApplicationConfigurationSection>
    <add key="DpiAwareness" value="PerMonitorV2" />
    <add key="EnableWindowsFormsHighDpiAutoResizing" value="true" />
  </System.Windows.Forms.ApplicationConfigurationSection>
</configuration>

二、强制激活原生DpiChanged事件

通过反射修改内部开关,绕开DpiHelper的条件限制:
在程序入口Program.cs的Application.Run执行前添加以下代码:

using System.Reflection;
using System.Windows.Forms;

static void Main()
{
    // 强制开启DpiChanged事件的内部触发开关
    Type dpiHelperType = typeof(Control).Assembly.GetType("System.Windows.Forms.DpiHelper");
    if (dpiHelperType != null)
    {
        FieldInfo enableEventField = dpiHelperType.GetField("EnableDpiChangedEvent", BindingFlags.NonPublic | BindingFlags.Static);
        enableEventField?.SetValue(null, true);
    }

    Application.EnableVisualStyles();
    Application.SetCompatibleTextRenderingDefault(false);
    Application.Run(new YourMainForm());
}

修改后顶层窗体的原生DpiChanged事件即可正常触发,DeviceDpi属性也会同步更新为当前最新DPI值。

三、无反射替代方案

如果生产环境不允许使用反射,可以基于已监听到的WM_DPICHANGED消息自定义封装事件:

public class DpiAwareBaseForm : Form
{
    /// <summary>
    /// 自定义DPI变化事件,替代原生不触发的DpiChanged
    /// </summary>
    public event EventHandler<DpiChangedEventArgs> CustomDpiChanged;

    private const int WM_DPICHANGED = 0x02E0;

    protected override void WndProc(ref Message m)
    {
        if (m.Msg == WM_DPICHANGED)
        {
            int oldDpi = DeviceDpi;
            int newDpi = (m.WParam.ToInt32() & 0xFFFF);
            CustomDpiChanged?.Invoke(this, new DpiChangedEventArgs(oldDpi, newDpi, Bounds));
            
            // 可选:手动更新DeviceDpi属性为最新值
            PropertyInfo deviceDpiProp = typeof(Control).GetProperty("DeviceDpi", BindingFlags.Instance | BindingFlags.NonPublic);
            deviceDpiProp?.SetValue(this, newDpi);
        }
        base.WndProc(ref m);
    }
}

所有需要监听DPI变化的窗体继承该基类,订阅CustomDpiChanged事件即可。

注意事项

  • .NET Framework版本的WinForms不会对DpiChanged事件做控件冒泡,子控件需要主动订阅父窗体的DPI变化事件
  • 不要在窗体构造函数中访问DeviceDpi属性,该属性在窗体渲染完成前只会返回程序启动时的初始DPI值

内容的提问来源于stack exchange,提问作者Sören Kuklau

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.13 16:09:03