C# PrintDocument用e.HasMorePages分页打印QRCode无限分页崩溃
问题根因
你的代码出现无限分页崩溃是3个核心逻辑错误导致的:
- 没有记录全局打印进度:
PrintPage事件每次触发时,你都从头遍历所有DataRow、从i=0开始循环二维码生成逻辑,完全没有用到BeginPrint里初始化的currentItem变量记录上一次打印停在了哪个二维码。第一次进入PrintPage时i=0,i%5 ==0成立,直接设置e.HasMorePages = true返回;下一次PrintPage触发后又从头开始跑循环,再次遇到i=0触发分页返回,无限重复这个流程就会导致程序卡死崩溃。 - 分页计数维度错误:你用的
i%5是单条DataRow内的二维码局部计数,不是所有待打印二维码的全局计数,跨数据行时计数会重置,根本无法准确统计单页打印的二维码数量。 - 重复冗余计算:每次触发
PrintPage都遍历全表计算总二维码数,既浪费性能,也没有把总数值和分页判断逻辑关联起来。
另外你的代码还存在GDI资源泄漏问题:生成的Bitmap二维码对象、Font对象用完没有释放,打印页数多了会占满内存加剧崩溃。
修复方案
核心思路是在全局维护打印进度状态,每次PrintPage只打印当前页最多5个二维码,打完后根据剩余待打印内容判断是否需要分页,不要每次从头遍历数据。
第一步:定义类级别的全局状态变量
// 记录当前已经打印到第几个二维码(全局索引) private int currentPrintIndex = 0; // 所有待打印二维码的内容列表(提前展平,避免遍历DataRow计数) private List<string> qrToPrint = new List<string>(); // 单页最多打印二维码数量(按你实测的A4容量设为5) private const int QrPerPage = 5; // 单个二维码块高度(200px二维码 + 20px下方文本间距) private const int QrBlockHeight = 220;
第二步:修改BeginPrint事件
在打印开始前一次性把所有要打印的二维码内容展平到列表,初始化打印进度,不要在PrintPage里重复计算总数:
private void printDocument1_BeginPrint(object sender, System.Drawing.Printing.PrintEventArgs e) { currentPrintIndex = 0; qrToPrint.Clear(); // 提前把所有需要打印的二维码按顺序存入列表 foreach (DataRow dr in dt.Rows) { // 跳过二维码内容、数量为空的行 if (dr[1] == DBNull.Value || string.IsNullOrWhiteSpace(dr[1].ToString())) continue; if (dr[4] == DBNull.Value || string.IsNullOrWhiteSpace(dr[4].ToString())) continue; string qrContent = dr[1].ToString(); int qrCount = Convert.ToInt32(dr[4].ToString()); // 按需要的数量把二维码内容加入打印列表 for (int i = 0; i < qrCount; i++) { qrToPrint.Add(qrContent); } } }
第三步:重写PrintPage事件逻辑
每次进入事件只处理当前页的内容,打满5个就标记分页返回,所有内容打完就结束打印:
private void printDocument1_PrintPage(object sender, System.Drawing.Printing.PrintPageEventArgs e) { using (Font fontNormal = new Font("Arial", 12, FontStyle.Regular)) { Brush textBrush = Brushes.Black; float left = e.MarginBounds.Left; float top = e.MarginBounds.Top; float currentY = top; int printedCount = 0; // 从上次打印结束的位置继续,不要从头开始 while (currentPrintIndex < qrToPrint.Count) { // 当前页已经打满5个,标记还有下一页,退出当前页打印 if (printedCount >= QrPerPage) { e.HasMorePages = true; return; } // 绘制二维码和下方文本,using自动释放Bitmap资源 string content = qrToPrint[currentPrintIndex]; using (Bitmap qrBmp = GenerateQRCODE(content)) { e.Graphics.DrawImage(qrBmp, left, currentY, 200, 200); } e.Graphics.DrawString(content, fontNormal, textBrush, left + 40, currentY + 180); // 更新位置和计数 currentY += QrBlockHeight; printedCount++; currentPrintIndex++; } // 所有二维码打印完成,没有后续页 e.HasMorePages = false; } }
说明
你项目中使用的PrintBar控件不影响上述分页逻辑,分页是PrintDocument组件的标准机制,和预览控件无关。如果实测单页能容纳的二维码数量和5有偏差,直接修改QrPerPage常量即可,调整QrBlockHeight可以适配不同的二维码、文本尺寸的间距。
内容的提问来源于stack exchange,提问作者Molnar Istvan
相关产品推荐
相关产品推荐

