printdocument 是 windows forms 本地打印的核心,需配合 printdialog 使用;printpage 事件中须用 e.graphics 绘制并设置 e.hasmorepages,避免耗时操作,优先使用 e.marginbounds 布局,预览效果不等于实际输出,务必真机测试。

Windows Forms 下用 PrintDocument 实现本地打印机输出,是最稳定、可控性最强的方式;WPF 或跨平台场景则需另寻方案,别硬套 WinForms 打印逻辑。
为什么不用 PrintDialog 直接打印文件?
很多人误以为 PrintDialog 自带“打印文档”能力,其实它只是个选择打印机和设置的弹窗。真正干活的是 PrintDocument —— 它负责把内容“画”到打印页上。
-
PrintDialog必须配合PrintDocument使用,单独调用ShowDialog()不会触发任何打印行为 - 常见错误:在
PrintDialog.ShowDialog() == DialogResult.OK后直接返回,忘了调用PrintDocument.Print() - 如果你要打印 PDF 或图片,得先用第三方库(如 iTextSharp、ImageSharp)解析/渲染成位图或 GDI+ 可绘制对象,再喂给
PrintDocument
PrintDocument.PrintPage 事件里必须干啥?
这是整个打印流程的核心回调,系统每次准备打一页就触发一次,你必须在这个事件里用 e.Graphics 把内容画出来,并通过 e.HasMorePages 告诉系统“还有下一页吗”。
- 不设置
e.HasMorePages = true,哪怕你有 10 页内容也只打第一页 - 别在该事件里做耗时操作(如读文件、查数据库),否则会卡住打印队列;提前加载好数据,只做绘制
-
e.MarginBounds是实际可打印区域(已扣除页边距),e.PageBounds是整页物理尺寸,日常绘图优先用前者 - 示例片段:
private void printDoc_PrintPage(object sender, PrintPageEventArgs e)
{
string text = "Hello from C#";
Font font = new Font("Arial", 12);
e.Graphics.DrawString(text, font, Brushes.Black, e.MarginBounds.Left, e.MarginBounds.Top);
e.HasMorePages = false; // 单页示例
}
如何让打印内容适配不同纸张和缩放?
硬编码坐标(比如写死 DrawString(..., 100, 100))在 A4 和 Letter 纸上会错位,甚至被截断。关键不是“缩放”,而是“按比例布局”。
- 用
e.MarginBounds.Width / e.MarginBounds.Height判断横竖版,避免文字被裁 - 计算字体大小时参考
e.Graphics.DpiX(通常 96 或 120),而不是固定像素值 - 如果内容是表格或 HTML 片段,建议先用
Graphics.MeasureString()测宽高,再分页;但注意该方法精度有限,复杂排版建议生成 PDF 后调用系统打印 - 不要依赖
PrintDocument.DefaultPageSettings的初始值,用户可能改过打印机默认设置,以e.PageSettings为准
最常被忽略的一点:打印预览窗口(PrintPreviewDialog)里的显示效果 ≠ 实际打印机输出效果——尤其是字体嵌入、灰度模式、双面设置等,真机测试不可跳过。调试时记得在 PrintPage 里加日志或断点,别只靠预览猜。










