C# WinForms中PrintDocument嵌套循环打印的分页问题
解决C# WinForms库存报表内层循环分页问题
核心思路
要处理商品存放位置跨页的情况,不能一次性打印完所有位置,必须在打印每个位置前检查页面剩余空间:空间不足时触发分页,打印新页眉后继续当前商品的剩余位置。关键是要跟踪打印进度——记录当前处理到哪个商品、该商品已打印多少个位置,确保分页后能从断点继续。
具体实现步骤
- 保存打印状态:在打印类中定义成员变量,比如
currentProductIndex(当前商品索引)、currentLocationIndex(当前商品已打印的位置索引),用于记录断点。 - 拆分打印逻辑:
- 在
PrintPage事件中,先打印当前商品的头部信息(如果是该商品的第一页或分页后)。 - 循环打印当前商品的剩余位置:每次打印前计算行高,判断是否超出页边距剩余空间。
- 空间足够:打印位置,更新
currentLocationIndex和当前Y坐标。 - 空间不足:设置
e.HasMorePages = true,退出当前事件,等待下一次触发(自动分页后继续打印剩余位置)。
- 空间足够:打印位置,更新
- 当前商品所有位置打印完成后,重置
currentLocationIndex,推进currentProductIndex处理下一个商品。
- 在
- 重置初始状态:在
BeginPrint事件中将currentProductIndex和currentLocationIndex重置为0,确保每次打印都从头开始。
示例代码
// 打印类的成员变量 private int currentProductIndex = 0; private int currentLocationIndex = 0; private List<InventoryProduct> products; // 你的商品列表集合 private void printDocument1_BeginPrint(object sender, PrintEventArgs e) { // 打印开始前重置进度 currentProductIndex = 0; currentLocationIndex = 0; } private void printDocument1_PrintPage(object sender, PrintPageEventArgs e) { float yPos = e.MarginBounds.Top; float lineHeight = e.Graphics.MeasureString("测试行", this.Font).Height; float pageBottom = e.MarginBounds.Bottom; // 从当前商品开始遍历 while (currentProductIndex < products.Count) { var product = products[currentProductIndex]; // 打印商品头部信息(首次处理该商品或分页后) if (currentLocationIndex == 0) { string headerText = $"{product.Name} ({product.Supplier}) PRODUCT #{product.ProductId} TOTAL ON HAND: {product.TotalOnHand}"; e.Graphics.DrawString(headerText, this.Font, Brushes.Black, e.MarginBounds.Left, yPos); yPos += lineHeight * 1.5f; // 头部后留间距 } // 打印当前商品的剩余存放位置 while (currentLocationIndex < product.Locations.Count) { var loc = product.Locations[currentLocationIndex]; string lineText = $" Aisle: {loc.Aisle} Count: {loc.Count}"; // 检查剩余空间是否足够打印当前行 if (yPos + lineHeight > pageBottom) { e.HasMorePages = true; return; // 触发分页,退出当前事件等待下一页打印 } e.Graphics.DrawString(lineText, this.Font, Brushes.Black, e.MarginBounds.Left, yPos); yPos += lineHeight; currentLocationIndex++; } // 当前商品打印完成,重置位置索引,处理下一个商品 currentLocationIndex = 0; currentProductIndex++; yPos += lineHeight; // 商品间留空行 } // 所有内容打印完毕 e.HasMorePages = false; } // 自定义商品和位置实体类示例 public class InventoryProduct { public string Name { get; set; } public string Supplier { get; set; } public string ProductId { get; set; } public int TotalOnHand { get; set; } public List<ProductLocation> Locations { get; set; } = new List<ProductLocation>(); } public class ProductLocation { public int Aisle { get; set; } public int Count { get; set; } }
关键注意事项
- 状态持久化:必须用类成员变量保存打印进度,不能用
PrintPage事件内的局部变量,因为每次分页触发的PrintPage是独立调用。 - 空间计算:要准确计算行高(包含字体大小和行间距),避免内容超出页边距。
- 页眉扩展:如果需要每页都打印通用页眉,可在
PrintPage事件开头先打印页眉内容,再处理商品和位置。
内容的提问来源于stack exchange,提问作者FrontRangeGuy
相关产品推荐
相关产品推荐

