C# WinForm DPI自适应方案

在现代高分辨率显示器上,DPI(每英寸点数)设置可能会导致传统的WinForm应用程序显示不正确,如控件大小和布局问题。为了确保应用程序在不同DPI设置下有良好的用户体验,必须实现DPI自适应。以下是一个详细的C# WinForm应用程序DPI自适应方案。

一、什么是DPI自适应

DPI自适应是指应用程序能够根据显示器的DPI设置自动调整控件的大小、字体和布局,使其在高DPI环境下显示正确。Windows提供了几种DPI处理模式:

  1. System DPI Awareness: 应用程序在启动时获取系统DPI并根据此DPI进行缩放。不能动态响应DPI更改。
  2. Per-Monitor DPI Awareness: 应用程序能够响应DPI更改并在不同的显示器上使用不同的DPI设置。

二、启用DPI Awareness

在WinForm应用程序中启用DPI Awareness,需要在应用程序的 app.manifest文件中进行设置。

  1. 编辑app.manifest文件

    打开项目中的 app.manifest文件(如果没有,可以添加一个)。在其中添加以下内容:

    <application xmlns="urn:schemas-microsoft-com:asm.v3">
     <windowsSettings>
       <dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true</dpiAware>
       <dpiAware xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">per monitor</dpiAware>
     </windowsSettings>
    </application>

    这段代码启用了Per-Monitor DPI Awareness。

三、在代码中处理DPI变化

  1. 处理DPI变化事件

    在WinForm应用程序中,可以通过处理窗口的 WM_DPICHANGED消息来响应DPI变化。在Form的代码中覆盖 WndProc方法:

    protected override void WndProc(ref Message m)
    {
       const int WM_DPICHANGED = 0x02E0;
       if (m.Msg == WM_DPICHANGED)
       {
           OnDpiChanged(m);
       }
       base.WndProc(ref m);
    }
    
    private void OnDpiChanged(Message m)
    {
       // 获取新的DPI值
       int newDpiX = (int)(m.WParam.ToInt32() & 0xFFFF);
       int newDpiY = (int)(m.WParam.ToInt32() >> 16);
    
       // 调整控件大小和字体
       float scaleFactor = newDpiX / 96.0f;
       foreach (Control control in this.Controls)
       {
           control.Font = new Font(control.Font.FontFamily, control.Font.Size * scaleFactor);
           control.Scale(new SizeF(scaleFactor, scaleFactor));
       }
    
       // 调整窗体大小
       Rectangle suggestedRect = (Rectangle)Marshal.PtrToStructure(m.LParam, typeof(Rectangle));
       this.Bounds = suggestedRect;
    }
  2. 调整控件大小和字体

    在DPI变化时,除了调整控件大小,还需要调整字体大小,以确保文本在不同DPI设置下显示正确。可以在 OnDpiChanged方法中完成这些调整。

四、测试和验证

在启用了DPI Awareness并实现了DPI变化处理后,需要在高DPI显示器上测试应用程序,以确保控件大小、布局和字体都能正确调整。可以在Windows设置中更改显示器DPI进行测试。

五、示例代码

以下是一个完整的示例代码,展示了如何在C# WinForm应用程序中实现DPI自适应:

using System;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Windows.Forms;

namespace DpiAwareApp
{
    public partial class MainForm : Form
    {
        public MainForm()
        {
            InitializeComponent();
        }

        protected override void WndProc(ref Message m)
        {
            const int WM_DPICHANGED = 0x02E0;
            if (m.Msg == WM_DPICHANGED)
            {
                OnDpiChanged(m);
            }
            base.WndProc(ref m);
        }

        private void OnDpiChanged(Message m)
        {
            // 获取新的DPI值
            int newDpiX = (int)(m.WParam.ToInt32() & 0xFFFF);
            int newDpiY = (int)(m.WParam.ToInt32() >> 16);

            // 调整控件大小和字体
            float scaleFactor = newDpiX / 96.0f;
            foreach (Control control in this.Controls)
            {
                control.Font = new Font(control.Font.FontFamily, control.Font.Size * scaleFactor);
                control.Scale(new SizeF(scaleFactor, scaleFactor));
            }

            // 调整窗体大小
            Rectangle suggestedRect = (Rectangle)Marshal.PtrToStructure(m.LParam, typeof(Rectangle));
            this.Bounds = suggestedRect;
        }
    }
}

六、总结

实现DPI自适应是确保WinForm应用程序在高分辨率显示器上有良好用户体验的重要步骤。通过在manifest文件中启用DPI Awareness,并在代码中处理DPI变化事件,可以实现应用程序对不同DPI设置的自适应。通过合理调整控件大小和字体,保证应用程序在各种DPI环境下都能正常显示和操作。


思维导图:C# WinForm DPI自适应方案

graph TD;
    A[DPI自适应方案] --> B[启用DPI Awareness]
    B --> B1[编辑app.manifest]
    A --> C[处理DPI变化事件]
    C --> C1[覆盖WndProc方法]
    C --> C2[调整控件大小和字体]
    A --> D[测试和验证]
    D --> D1[高DPI显示器测试]
    D --> D2[调整DPI设置]
    A --> E[示例代码]
    E --> E1[完整示例]

通过以上步骤和代码示例,开发者可以在C# WinForm应用程序中实现DPI自适应,确保应用程序在高分辨率显示器上显示良好。