Graphics.MeasureString 测量不准因含文字边缘补偿和行距预留,应优先用 TextRenderer.MeasureText 或 Graphics.MeasureString 配 StringFormat.GenericTypographic;自动换行需精确计算可用宽度、统一字体上下文及 DPI,并按空格/标点分段+二分逼近断点;WPF/UWP 需分别使用 FormattedText/TextLayout。
Graphics.MeasureString 算不准,别直接用
它返回的宽度常比实际渲染宽,尤其在 winforms 或 gdi+ 绘图场景下,measurestring 默认带「文字边缘补偿」和「行距预留」,不是纯字符盒宽度。你拿它切分自动换行,大概率会提前折行或撑出控件边界。
- 真实排版需用
TextRenderer.MeasureText(GDI,更贴近控件渲染逻辑) - 若必须用
Graphics.MeasureString,加StringFormat.GenericTypographic并设format.Trimming = StringTrimming.None - WinForms 中
Label/TextBox内部用的是 GDI,不是 GDI+,所以优先对齐TextRenderer
自动换行得先定好“可用宽度”和“字体上下文”
字符串宽度不是绝对值,它依赖当前绘制设备的 DPI、字体的 FontStyle(比如 Bold 会加粗变宽)、甚至 Graphics.PageUnit 设置。漏掉任一,测出来就是错的。
-
可用宽度要减去左右 padding、border 宽度(比如Panel.ClientRectangle.Width,不是Width) - 传给
TextRenderer.MeasureText的Font必须和最终绘制时完全一致(包括FontSize、GdiCharSet、是否启用TextRenderingHint.ClearTypeGridFit) - 如果目标是高 DPI 缩放界面,记得用
Graphics.DpiX校验单位一致性,别硬写像素值
逐字测量太慢?用二分查找切分长文本
对几百字以上字符串做「从头累加测宽」很容易卡 UI,尤其在滚动或动画中反复调用。但全量预计算又浪费内存——折中方案是按词/按空格分段后二分逼近断点。
- 先按空格或标点(如
",。!?;:")切分string[],避免在单词中间断 - 对每段用
TextRenderer.MeasureText测宽,累计超限时回退到上一个分隔符 - 若要求严格对齐(如 PDF 导出),再对最后一段用二分法在字符级微调:
MeasureText(text.Substring(0, mid))对比阈值 - 注意:中文无空格,此时可按 Unicode 字符块(如 \u4e00-\u9fff)视为等宽单元,但字号 >12pt 时仍建议逐字测
WPF / UWP 场景别混用 GDI 测量函数
WPF 用的是 DirectX 渲染管线,FormattedText 才是正解。拿 TextRenderer.MeasureText 去算 TextBlock 的换行位置,结果可能差 1–3 像素,尤其在 ClearType 开启时。
- WPF 中用
new FormattedText(...).Width,构造时必须传入VisualTreeHelper.GetDpi(visual)获取的 DPI -
FormattedText的MaxTextWidth可直接驱动自动换行逻辑,无需手动切分 - UWP 用
TextLayout+TextParagraphProperties,TextBlock的ActualWidth在加载后才可靠,别在Loaded之前读
最麻烦的其实是混合场景:比如 WinForms 内嵌 WPF 元素,或者用 SkiaSharp 渲染。这时候宽度基准必须统一到物理像素,并显式禁用所有字体缩放补偿——否则同一个 Font 实例,在不同上下文里测出来能差 8%。










