自动缩放但仍处理 WM_DPICHANGED

2024-04-22

我在使用 C# 编写的非常复杂的 WinForms 应用程序时遇到了一些问题。我希望应用程序在 DPI 更改时让 Windows 自动缩放,但我仍然需要挂钩 WM_DPICHANGED 事件才能缩放一些自定义绘制的文本。

困境是,如果我让应用程序 DPI 不知道,WM_DPICHANGED 消息永远不会在 DefWndProc 中被拦截,并且永远无法检索正确的 DPI 比例,但表单会按照我想要的方式“自动缩放”。但是,如果我使应用程序 DPI Aware,则 WM_DPICHANGED 消息将被拦截,并且可以计算正确的 DPI,但表单不会“自动缩放”。

正如我所说,该应用程序非常复杂,并且使用了大量第三方控件,因此我无法花时间在 WPF 中重新编写该应用程序或尝试自己扩展该应用程序。

如何让应用程序拦截 WM_DPICHANGED 消息、计算正确的 DPI 并仍然允许 Windows 管理表单缩放?

程序.cs:

static class Program
{       
    [STAThread]
    static void Main()
    {
        if (Environment.OSVersion.Version.Major >= 6)
        {
            // If the line below is commented out then the app is no longer DPI aware and the 
            // WM_DPICHANGED event will never fire in the DefWndProc of the form
            int retValue = SetProcessDpiAwareness(ProcessDPIAwareness.ProcessPerMonitorDPIAware);

        }

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

    private enum ProcessDPIAwareness
    {
        ProcessDPIUnaware = 0,
        ProcessSystemDPIAware = 1,
        ProcessPerMonitorDPIAware = 2
    }

    [DllImport("shcore.dll")]
    private static extern int SetProcessDpiAwareness(ProcessDPIAwareness value);
}

表格1.cs:

protected override void DefWndProc(ref Message m)
    {
        switch (m.Msg)
        {
            case 0x02E0: //WM_DPICHANGED
                {                       
                    int newDpi = m.WParam.ToInt32() & 0xFFFF;
                    float scaleFactor = (float)newDpi / (float)96;                      
                }
                break;
        }

        base.DefWndProc(ref m);
    }

UPDATE:我使用的是具有多显示器设置的 Windows 10。所有显示器均为同一型号,基本分辨率为 1920x1080。我使用显示设置将其中一台显示器的尺寸设置为 125%。


而不是捕获WM_DPICHANGED事件,只要在需要时询问当前的 DPI 设置(在Paint事件或其他)?

但这也并不明显。如果你搜索 StackOverflow,通常可以找到以下答案:

using (Graphics screen = Graphics.FromHwnd(IntPtr.Zero))
{
    IntPtr hdc = screen.GetHdc();
    int dpiX = GetDeviceCaps(hdc, DeviceCaps.LOGPIXELSX);
    screen.ReleaseHdc(hdc);
}

但是,无论实际 DPI 设置如何,它都将始终返回 96,除非...

  • 您使用 Windows XP 或在 DPI 设置中签入兼容模式。Problem:您不能对用户强制执行它。
  • DWM 已关闭(您使用基本或经典主题)。Problem: 和上面一样。
  • 你打电话设置进程DPIAware https://msdn.microsoft.com/en-us/library/ms633543%28v=vs.85%29.aspx使用 GetDeviceCaps 之前的函数。Problem:该函数应该在所有其他渲染之前调用一次。如果您现有不支持 DPI 的应用程序,则更改感知会破坏整个外观。一旦调用该函数,它就无法关闭。
  • 你打电话设置进程Dpi感知 https://msdn.microsoft.com/en-us/library/dn302216%28v=vs.85%29.aspx使用 GetDeviceCaps 之前和之后。Problem:此功能至少需要Windows 8.1

真正可行的解决方案

看来GetDeviceCaps 函数 https://msdn.microsoft.com/en-us/library/windows/desktop/dd144877%28v=vs.85%29.aspxMSDN 上没有完整记录。至少我发现pinvoke.net http://pinvoke.net/default.aspx/gdi32/GetDeviceCaps.html提到了该函数可以获得的一些其他选项。最后我得出了以下解决方案:

public static int GetSystemDpi()
{
    using (Graphics screen = Graphics.FromHwnd(IntPtr.Zero))
    {
        IntPtr hdc = screen.GetHdc();

        int virtualWidth = GetDeviceCaps(hdc, DeviceCaps.HORZRES);
        int physicalWidth = GetDeviceCaps(hdc, DeviceCaps.DESKTOPHORZRES);
        screen.ReleaseHdc(hdc);

        return (int)(96f * physicalWidth / virtualWidth);
    }
}

以及上面示例中所需的附加代码:

private enum DeviceCaps
{
    /// <summary>
    /// Logical pixels inch in X
    /// </summary>
    LOGPIXELSX = 88,

    /// <summary>
    /// Horizontal width in pixels
    /// </summary>
    HORZRES = 8,

    /// <summary>
    /// Horizontal width of entire desktop in pixels
    /// </summary>
    DESKTOPHORZRES = 118
}

/// <summary>
/// Retrieves device-specific information for the specified device.
/// </summary>
/// <param name="hdc">A handle to the DC.</param>
/// <param name="nIndex">The item to be returned.</param>
[DllImport("gdi32.dll")]
private static extern int GetDeviceCaps(IntPtr hdc, DeviceCaps nIndex);
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

自动缩放但仍处理 WM_DPICHANGED 的相关文章

随机推荐

  • Haskell:单词,取消单词分隔符

    有什么办法可以提供分隔符words and unwords在haskell中 使其类似于python中的split和join 另请查看友好的包裹split 它提供了一个模块Data List Split http hackage haske
  • 从 ANT 运行 BAT 文件

    我浏览了论坛上的许多帖子 但无法整理出来 我正在尝试从 ANT 脚本运行 BAT 文件 文件夹层次结构是这样的 Project build xml build C test bat 我编写的 ANT 文件是
  • Railstutorial.org - 未定义的方法“工厂”

    我正在尝试关注 Railstutorial org 目前正在阅读第 7 章 您将在其中开始使用工厂 http railstutorial org chapters modeling and viewing users two sec tes
  • 如何在android中压缩文件夹以制作docx文件?

    我正在尝试制作一个 Android 应用程序 可以打开 docx 文件来读取 编辑和保存它 我的想法是将存档中的所有 xml 文件提取到临时文件夹中 在这个文件夹中我们可以编辑docx的内容 word document xml 问题是当我压
  • java中的for循环和i的值

    在 Horstmann 的 java 教科书 Big Java Late Objects 中 对于 for 循环 例如 for i 0 i lt 5 i 霍斯特曼说 价值观i对于这个 for 循环是0 1 2 3 4 5 然而 在我看来 i
  • Angular UI Bootstrap Popover - 如何添加关闭按钮

    我有一个表格 每个单元格都有一个弹出窗口 如下例所示 对弹出窗口的调用 td td
  • 使用额外模块构建 opencv 错误 ocv_download

    我尝试使用 Extramodules 构建 openCV 如果我尝试使用 cmake 配置项目文件 Windows 10 上的 vc14 x32 则会出现错误 opencv contrib master modules xfeatures2
  • Angular 4显示当前时间

    在 Angular 4 变化检测系统中显示当前时间的正确 规范 方法是什么 问题如下 根据定义 当前时间每时每刻都在不断变化 但 Angular 4 变更检测系统无法检测到它 因此 我认为有必要明确调用ChangeDetectorRef d
  • 如何忽略 Rails 中特定操作的真实性令牌?

    当我不想检查真实性令牌的特定操作时 如何告诉 Rails 跳过检查它 轨道 5 2 您可以使用相同的skip before action https api rubyonrails org classes ActionController
  • C# OpenFileDialog 非模态可能

    是否可以创建 拥有非模式 net OpenFileDialog 我在主对话框中有一个 UI 元素 始终需要可供用户按下 No 打开文件对话框 http msdn microsoft com en us library system wind
  • 领域未获取数据

    我在使用领域时遇到问题 findAll 和 findAllAsync 不会从领域返回任何数据 我正在像这样从主线程更新领域对象 public void updatePhoto final int ticketID realm beginTr
  • 合并多列上的两个 pandas 数据框

    我有两个数据框 gt gt gt df1 Output col1 col2 col3 col4 a abc 10 str1 b abc 20 str2 c def 20 str2 d abc 30 str2 gt gt gt df2 Out
  • Spring boot 2.0.2,使用Spring数据如何从实体验证中获取消息

    我正在构建一个 Spring Boot 2 0 2 Web 服务 实体中有许多我不想为空的字段 当尝试保留具有无效字段的实体时 如何从该特定字段获取消息 例如 我有一个实体 Entity Table name users public cl
  • 嵌入资源名称

    在C 中 嵌入资源名称的默认行为是这样的
  • 重定向后执行函数 - javascript

    好的 我的页面 MyPage 上有一个简单的按钮 可以淡出当前 div fade 1 并淡入另一个 div fade 2 我现在意识到 我可能想直接从其他地方转到该页面 淡出 2 我可以通过以下方式重定向我的页面window locatio
  • NodeJS Mongoose 总是返回一个空数组

    我尝试过使用find and findOne并且两者都没有返回文件 find返回一个空数组findOne正在返回null err在这两种情况下null以及 这是我的连接 function connectToDB mongoose conne
  • 有没有一种优雅的方法可以在 Django 管理中为 M2M 字段设置 list_filter ?

    如果我有一个披萨模型和一个浇头模型 它们之间有 m2m 是否有一些快速优雅的方法可以为它们中的任何一个添加到管理列表页面 为包含特定浇头 包含的所有浇头的所有比萨饼添加列表过滤器在某个披萨里 内置的 list filter 不支持 m2m
  • 重命名 cassandra 1.2 中的键空间和列族

    如何在 cassandra 1 2 中重命名键空间和列族 我知道不再支持 cassandra cli 重命名 api 如何在 Cassandra 中重命名键空间 https stackoverflow com questions 76491
  • 谷歌地图响应式调整大小

    我试图让谷歌地图响应并调整大小 同时在窗口调整大小时保持其中心 我阅读了其他堆栈问题 例如 响应式谷歌地图 https stackoverflow com questions 15421369 responsive google map a
  • 自动缩放但仍处理 WM_DPICHANGED

    我在使用 C 编写的非常复杂的 WinForms 应用程序时遇到了一些问题 我希望应用程序在 DPI 更改时让 Windows 自动缩放 但我仍然需要挂钩 WM DPICHANGED 事件才能缩放一些自定义绘制的文本 困境是 如果我让应用程