如何让.NET 6 Windows Forms忽略DPI缩放以适配像素渲染?
解决Windows Forms + SharpDX RenderForm 像素级渲染的DPI缩放问题
1. 启用Per-Monitor DPI Awareness v2(核心步骤)
这是让应用程序精准感知显示器缩放比例的基础,旧模式无法满足像素级需求:
方法A:修改应用程序清单
在项目的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>
确保清单已嵌入程序集(项目属性→应用程序→资源→清单选择对应文件)。
方法B:代码强制设置(清单失效时备用)
在Main方法最开头调用:
using System.Runtime.InteropServices; [DllImport("user32.dll")] private static extern bool SetProcessDpiAwarenessContext(int dpiFlag); // 程序启动时执行 SetProcessDpiAwarenessContext(-4); // 对应DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2
2. 自定义RenderForm实现像素尺寸锁定
SharpDX的RenderForm默认会跟随系统缩放,需重写逻辑确保客户端区域严格匹配目标像素:
using SharpDX.Windows; using System.Windows.Forms; public class PixelRenderForm : RenderForm { private int _targetPixelWidth; private int _targetPixelHeight; public PixelRenderForm(string title) : base(title) { AutoScaleMode = AutoScaleMode.None; FormBorderStyle = FormBorderStyle.FixedSingle; // 可选,避免拖拽缩放干扰 } // 设置目标像素尺寸 public void SetPixelSize(int width, int height) { _targetPixelWidth = width; _targetPixelHeight = height; SyncFormSizeWithDpi(); } private void SyncFormSizeWithDpi() { // 计算当前DPI缩放比例(96为标准DPI) var dpiScale = DeviceDpi / 96f; // 转换为窗体客户端区域的逻辑尺寸,确保缩放后正好是目标像素 var clientSize = new Size((int)(_targetPixelWidth / dpiScale), (int)(_targetPixelHeight / dpiScale)); // 调整窗体整体尺寸(包含边框) Size = ClientSizeToSize(clientSize); } protected override void OnDpiChanged(DpiChangedEventArgs e) { base.OnDpiChanged(e); SyncFormSizeWithDpi(); // 触发渲染目标更新事件 PixelSizeChanged?.Invoke(this, new Size(_targetPixelWidth, _targetPixelHeight)); } public event EventHandler<Size> PixelSizeChanged; }
使用时直接调用SetPixelSize(1920, 1080),即可确保客户端区域为1920×1080物理像素。
3. 同步DirectX渲染目标尺寸
在初始化和DPI变化时,基于目标像素尺寸创建渲染目标:
var renderForm = new PixelRenderForm("Pixel-Perfect DirectX"); renderForm.SetPixelSize(1920, 1080); // DPI变化时更新渲染目标 renderForm.PixelSizeChanged += (s, size) => { // 销毁旧渲染目标 // ... // 创建新渲染目标,尺寸为size.Width × size.Height // ... }; // 渲染循环中设置视口为像素尺寸 var viewport = new SharpDX.Mathematics.Interop.RawViewportF(0, 0, renderForm._targetPixelWidth, renderForm._targetPixelHeight); deviceContext.Rasterizer.SetViewport(viewport);
4. 清理WinForms自动缩放残留
在Program.cs中确保DPI模式正确:
ApplicationConfiguration.Initialize(); Application.SetHighDpiMode(HighDpiMode.PerMonitorV2);
内容的提问来源于stack exchange,提问作者LOST
相关产品推荐
相关产品推荐

