PrintDocument 是 Windows Forms 中需手动绘制每页的精确打印方案,核心为 PrintPage 事件循环:Print() 触发 → 绘制当前页 → 设 e.HasMorePages 决定是否继续;须预加载数据、用 MeasureString 预估高度分页、显式设置 StringFormat 对齐、确认中文字体、统一用 MarginBounds 处理页眉页脚,并经多打印机实测验证。

PrintDocument 是 Windows Forms 中最直接、最可控的打印方案,但它的“可控”意味着你得亲手画每一页——它不自动排版、不解析 HTML 或 PDF,只提供绘图上下文。如果你需要精确控制字体、位置、分页逻辑,或者已有报表数据结构,它很合适;如果只是想快速打印一个 DataGridView 或 RichTextBox 的内容,反而容易绕远路。
PrintDocument 的核心流程:从准备到触发
打印不是调用一个函数就完事,而是三步闭环:PrintDocument.Print() 触发 → 系统调用 PrintPage 事件 → 你在事件里用 e.Graphics 绘制当前页 → 返回 e.HasMorePages = true/false 决定是否继续。
- 必须订阅
PrintPage事件,否则什么都不会输出 -
e.Graphics是 GDI+ 绘图对象,所有文字、线条、图片都靠它画,不支持 WPF 渲染或 HTML 标签 - 页边距、纸张尺寸由
PrinterSettings和DefaultPageSettings控制,不能在PrintPage里临时改纸张大小 - 如果数据量大(比如上千行报表),别在
PrintPage里反复查数据库——提前加载、缓存、按页切片
画文字和表格时的关键参数与避坑点
用 e.Graphics.DrawString() 打印文字看着简单,但换行、对齐、截断全是手动活;画表格更是没有“自动列宽”这种东西。
-
StringFormat必须显式设置Alignment和LineAlignment,否则默认左上对齐,跨行文本容易错位 - 中文字符要确认字体是否嵌入了中文字体(比如用
new Font("SimSun", 10)而不是"Arial"),否则可能显示方块或空白 - 计算文本宽度要用
e.Graphics.MeasureString(),但注意它测的是逻辑尺寸,实际打印可能因 DPI 缩放偏移,建议留 1–2mm 余量 - 画表格线推荐用
Pen+DrawLine(),别依赖DrawRectangle()填充——边框粗细和接缝对齐很难控制
如何安全处理分页和长内容
分页不是 PrintDocument 自动做的,是你在 PrintPage 事件里根据已绘制高度判断是否翻页。常见错误是“画超了再退回去”,结果最后一行被截断或重复。
- 每次进入
PrintPage事件前,先检查剩余数据起始索引(比如currentRow)是否越界,越界则设e.HasMorePages = false - 不要在循环里无条件
DrawString(),而应先用MeasureString()预估下一行高度,加上当前y坐标后,对比e.MarginBounds.Bottom - 页眉页脚建议统一用
e.MarginBounds.Top和e.MarginBounds.Bottom为基准,而不是硬编码像素值 - 如果某条记录内容过长(如大段备注),需手动拆行:用
Graphics.MeasureString(text, font, width)配合TextRenderer.MeasureText()更准确
真正难的不是画出来,而是让第 57 页的表格头和第 1 页对齐、让跨页的合计数不出错、让不同打印机的实际输出和预览一致。这些细节不会报错,但会在线上环境突然暴露——建议至少用两种真实打印机(激光/针式)、两种纸张尺寸跑一次全流程验证。



















