WinForms中ContextMenuStrip的DPI感知异常问题求助
1. 确认项目DPI感知配置完全生效
首先确保项目的DPI配置正确且生效:
- 检查csproj文件中的配置:
<ApplicationHighDpiMode>PerMonitorV2</ApplicationHighDpiMode>
- 在
Program.cs中手动设置进程DPI感知(避免SDK自动处理的遗漏):
[System.Runtime.InteropServices.DllImport("user32.dll")] private static extern bool SetProcessDpiAwarenessContext(int dpiFlag); static void Main() { // 启用PerMonitorV2模式 SetProcessDpiAwarenessContext(-4); ApplicationConfiguration.Initialize(); Application.Run(new MainForm()); }
2. 监听窗口DPI变更,强制更新ContextMenuStrip
ContextMenuStrip不会自动跟随窗口DPI变更,需要在窗口的DpiChanged事件中手动更新:
在窗体代码中添加以下逻辑:
protected override void OnDpiChanged(DpiChangedEventArgs e) { base.OnDpiChanged(e); UpdateContextMenuStripDpi(yourContextMenuStrip, e.DeviceDpiNew); } private void UpdateContextMenuStripDpi(ContextMenuStrip cms, int newDpi) { float scaleFactor = newDpi / 96f; // 更新菜单字体 cms.Font = new Font(cms.Font.FontFamily, cms.Font.Size * scaleFactor, cms.Font.Style); // 递归更新所有子项及子菜单 cms.SuspendLayout(); foreach (ToolStripItem item in cms.Items) { if (item is ToolStripMenuItem menuItem && menuItem.DropDown is ContextMenuStrip subCms) { UpdateContextMenuStripDpi(subCms, newDpi); } item.Invalidate(); } cms.ResumeLayout(true); cms.PerformLayout(); }
3. 修复勾选标记的缩放问题
ToolStripMenuItem的勾选标记默认不会随DPI缩放,需要手动生成适配尺寸的标记图像:
添加以下方法,并在UpdateContextMenuStripDpi中调用:
private void UpdateMenuItemCheckMark(ToolStripMenuItem item, int dpi) { float scale = dpi / 96f; int newSize = (int)(16 * scale); // 默认勾选标记尺寸为16x16,按DPI缩放 using (Bitmap bmp = new Bitmap(newSize, newSize)) using (Graphics g = Graphics.FromImage(bmp)) { g.ScaleTransform(scale, scale); // 绘制系统默认勾选标记 ToolStripRenderer.Default.DrawItemCheck(g, new ToolStripItemImageRenderEventArgs(g, item, SystemIcons.Check.ToBitmap(), new Point(0, 0))); item.Image = new Bitmap(bmp); } }
修改UpdateContextMenuStripDpi中的循环逻辑:
if (item is ToolStripMenuItem menuItem) { UpdateMenuItemCheckMark(menuItem, newDpi); if (menuItem.DropDown is ContextMenuStrip subCms) { UpdateContextMenuStripDpi(subCms, newDpi); } item.Invalidate(); }
4. 处理跨显示器移动后的尺寸还原
监听窗体的LocationChanged事件,确保窗口移动时实时检查当前显示器DPI并更新:
public MainForm() { InitializeComponent(); LocationChanged += MainForm_LocationChanged; } private void MainForm_LocationChanged(object sender, EventArgs e) { var currentScreen = Screen.FromControl(this); UpdateContextMenuStripDpi(yourContextMenuStrip, currentScreen.DeviceDpi); }
内容的提问来源于stack exchange,提问作者Daniel
相关产品推荐
相关产品推荐

