OwnerDraw模式ListBox中e.Bounds.Height与ItemHeight不匹配问题
问题原因及解决方案
核心原因
这种差异是高DPI缩放适配问题导致的:
- 台式机大概率开启了高DPI缩放(比如125%、150%),而笔记本的缩放比例更低或者未开启;
- 程序未启用高DPI感知时,WinForms系统会对整个控件进行自动缩放,你手动设置的
ItemHeight是基于原始DPI计算的,但实际渲染时系统会按缩放比例放大e.Bounds.Height,导致两者数值不匹配; - 构造函数中使用的
Font可能还未完成高DPI适配,TextRenderer.MeasureText获取的是未缩放的文本尺寸,进一步加剧了数值偏差。
解决方法
启用程序高DPI感知
在项目的app.config中添加以下配置,让程序感知系统DPI缩放:<appSettings> <add key="EnableWindowsFormsHighDpiAutoResizing" value="true" /> </appSettings> <system.windows.forms.applicationconfiguration> <add key="DpiAwareness" value="PerMonitorV2" /> </system.windows.forms.applicationconfiguration>或者在
Program.cs的入口处添加代码:using System.Windows.Forms; [STAThread] static void Main() { Application.SetHighDpiMode(HighDpiMode.PerMonitorV2); Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); Application.Run(new YourMainForm()); }基于DPI缩放因子计算ItemHeight
获取当前控件所在显示器的DPI缩放比例,用该比例调整计算出的ItemHeight,且避免在构造函数中计算(此时控件未完成高DPI适配),建议在OnLoad或OnFontChanged事件中处理:protected override void OnLoad(EventArgs e) { base.OnLoad(e); using (Graphics g = CreateGraphics()) { float dpiScale = g.DpiY / 96f; // 96为标准DPI值 Size size = TextRenderer.MeasureText("ABCxyz", Font); ItemHeight = (int)((size.Height * 2 + 1) * dpiScale); } }同步重绘逻辑
在OnDrawItem中,基于手动设置的ItemHeight调整绘制区域,确保和预期一致:protected override void OnDrawItem(DrawItemEventArgs e) { Rectangle adjustedBounds = new Rectangle(e.Bounds.X, e.Bounds.Y, e.Bounds.Width, ItemHeight); base.OnDrawItem(new DrawItemEventArgs(e.Graphics, e.Font, adjustedBounds, e.Index, e.State, e.ForeColor, e.BackColor)); }
内容的提问来源于stack exchange,提问作者Jonathan Wood
相关产品推荐
相关产品推荐

