在C#开发中,文字绘制看似简单,但要想让文字在各种场景下都清晰、准确、稳定地显示,其实是门技术活。不少开发者都遇到过这样的场景:明明代码写得没什么问题,可文字显示出来就是模糊、位置不对,甚至干脆乱码。今天就来聊聊几个常见的“坑”和对应的解决方案,希望能帮你少走弯路。

Graphics.DrawString 画出来的文字模糊或缩放失真
直接使用 Graphics.DrawString 在窗体或控件上绘图时,如果目标 Graphics 来自缩放后的设备上下文(比如高 DPI 显示器、窗体被放大、或通过 Bitmap 缩放后获取的 Graphics),文字很容易出现锯齿、发虚、边缘毛刺。这通常不是字体本身的问题,而是默认未启用高质量文本渲染。
问题出在默认的渲染设置上,我们需要手动指示它“认真对待”文字渲染。具体做法是显式设置文本渲染提示和插值模式:
- 调用
graphics.TextRenderingHint = System.Drawing.Text.TextRenderingHint.ClearTypeGridFit;(推荐用于屏幕显示)或AntiAliasGridFit(适合小字号) - 避免设置
graphics.InterpolationMode或SmoothingMode来影响文字——它们对DrawString无效,反而可能干扰后续图形绘制 - 如果是在
Bitmap上绘制后再缩放显示,更稳妥的做法是“按目标尺寸创建 Bitmap”而非“绘制后缩放”,否则字体 hinting 信息会丢失,文字自然就糊了
MeasureString 返回宽度不准,导致文字截断或布局错位
Graphics.MeasureString 也是一个经典“坑”。它返回的尺寸包含额外空白(如行距、左右留白),而且受 StringFormat 和当前 Graphics 的 PageUnit、DpiX 影响,并不等于实际绘制区域所需的空间。这就导致依赖它计算布局时,文字可能被截断或位置偏移。
更可靠的方式是改用 TextRenderer.MeasureText(WinForms 场景)或 Graphics.MeasureCharacterRanges(需要精确到字符区间时):
TextRenderer.MeasureText("abc", font, new Size(int.MaxValue, int.MaxValue), TextFormatFlags.NoPadding)—— 更贴近 GDI+ 实际排版,且不受Graphics当前状态干扰- 如果必须用
MeasureString,务必传入与DrawString相同的StringFormat实例,并确认Graphics.PageUnit == GraphicsUnit.Pixel - 注意:.NET 6+ 中
Graphics.MeasureString在某些 DPI 下行为有变更,旧代码迁移时建议回归测试
在 WPF 或 .NET MAUI 中混用 System.Drawing 绘制文字
WPF 原生用 FormattedText / DrawText,MAUI 用 Canvas 或 SKCanvas(SkiaSharp)。强行在 WriteableBitmap 或 SKSurface 上桥接 System.Drawing.Graphics 会导致性能暴跌甚至崩溃——因为 System.Drawing 是 GDI 封装,与 GPU 渲染管线根本不在一个赛道上。
正确的做法是直接放弃对 Graphics 的依赖,转用平台亲生的文本绘制方案:
- WPF:
var ft = new FormattedText("text", CultureInfo.CurrentCulture, FlowDirection.LeftToRight, new Typeface("Segoe UI"), 14, Brushes.Black);,再用ctx.DrawText(ft, point) - MAUI + SkiaSharp:
canvas.DrawText("text", x, y, paint),其中paint设置TextSize、Typeface和IsAntialias = true - 如果已有大量
System.Drawing逻辑,可以考虑封装为独立Bitmap绘制步骤,再作为图像贴到 WPF/MAUI 控件中(但会失去矢量缩放能力)
中文乱码或字体 fallback 失败
在非中文系统(如英文 Windows)上调用 DrawString 显示中文,即使指定了 "SimSun" 或 "Microsoft YaHei",仍可能回退到方块或空格——因为 Graphics 默认使用系统 UI 字体族 fallback,而 Font 构造函数指定的字体名只是“首选”,并不强制生效。
问题出在几个关键环节上:
- 不要依赖字体名字符串,改用
new Font(new FontFamily("Microsoft YaHei"), 12),确保FontFamily实例化成功(可先用FontFamily.Families检查是否在系统中存在) - 设置
StringFormat.GenericTypographic可提升多语言混合排版稳定性(尤其含标点、数字、中英文混排时) - Linux/macOS 上(.NET Core/.NET 5+),
System.Drawing依赖 libgdiplus,中文需预装对应字体(如fonts-wqy-zenhei)并验证FontFamily.IsA vailable
字体不是“设了就显示”,它是运行时资源绑定过程。出问题时先查 FontFamily.IsA vailable 和系统实际安装路径,别只盯着 DrawString 参数。