C# WinForms中使用PrintDocument嵌套循环打印的棘手问题
C# WinForms 拣货单嵌套循环打印分页解决方案
问题核心是PrintPage事件每次触发都会重新执行代码逻辑,直接在内层循环中设置ev.HasMorePages=true会导致重复处理当前商品和仓位,引发无限循环。解决思路是用类级变量保存打印进度,每次触发PrintPage时从上次中断的位置继续打印。
具体实现步骤
1. 定义类级状态变量
用来记录当前打印到哪一个商品、该商品下的第几个仓位,以及当前打印的Y坐标:
// 拣货商品实体(根据实际业务调整字段) public class PickItem { public string Name { get; set; } public string UPC { get; set; } public int TotalQuantity { get; set; } public List<PickLocation> Locations { get; set; } = new List<PickLocation>(); } public class PickLocation { public string Code { get; set; } public int Quantity { get; set; } } // 类级打印状态变量 private int _currentItemIndex = 0; private int _currentLocationIndex = 0; private float _currentPrintY; private List<PickItem> _currentPickList;
2. 初始化打印状态
在触发打印的按钮事件中,重置状态变量并加载拣货单数据:
private void btnPrintPickList_Click(object sender, EventArgs e) { // 重置打印进度 _currentItemIndex = 0; _currentLocationIndex = 0; // 初始Y坐标为页面上边距 _currentPrintY = printDocument1.DefaultPageSettings.Margins.Top; // 加载拣货单数据(替换为实际数据获取逻辑) _currentPickList = GetWarehousePickListData(); // 启动打印 printDocument1.Print(); }
3. 实现PrintPage事件逻辑
从上次中断的位置开始打印,每打印一行检查是否超出页面可打印区域:
private void printDocument1_PrintPage(object sender, PrintPageEventArgs ev) { var printArea = ev.MarginBounds; // 计算行高(含行间距,根据字体调整) float lineHeight = ev.Graphics.MeasureString("测试文本", this.Font).Height + 4; var boldFont = new Font(this.Font, FontStyle.Bold); while (_currentItemIndex < _currentPickList.Count) { var currentItem = _currentPickList[_currentItemIndex]; // 打印商品头部信息(加粗区分) string itemHeader = $"商品名称:{currentItem.Name} | UPC:{currentItem.UPC} | 总拣货量:{currentItem.TotalQuantity}"; ev.Graphics.DrawString(itemHeader, boldFont, Brushes.Black, printArea.Left, _currentPrintY); _currentPrintY += lineHeight * 1.5f; // 头部行高稍大,区分内容 // 打印当前商品的仓位列表,从上次中断的仓位索引开始 while (_currentLocationIndex < currentItem.Locations.Count) { var location = currentItem.Locations[_currentLocationIndex]; string locationLine = $" 仓位编码:{location.Code} | 拣货数量:{location.Quantity}"; // 检查当前行是否会超出页面底部 if (_currentPrintY + lineHeight > printArea.Bottom) { // 标记还有未打印内容,退出当前事件,等待下一次PrintPage触发 ev.HasMorePages = true; return; } ev.Graphics.DrawString(locationLine, this.Font, Brushes.Black, printArea.Left, _currentPrintY); _currentPrintY += lineHeight; _currentLocationIndex++; } // 当前商品的所有仓位打印完成,重置仓位索引,切换到下一个商品 _currentLocationIndex = 0; _currentItemIndex++; // 商品之间添加空白行分隔 _currentPrintY += lineHeight; } // 所有内容打印完成,无更多页面 ev.HasMorePages = false; }
关键注意事项
- 状态变量必须是类级:不能在PrintPage事件内部定义,否则每次触发都会重置,无法保存进度。
- 页边距使用
ev.MarginBounds:确保打印内容在打印机可识别的可打印区域内,避免被裁切。 - 打印前重置状态:如果用户多次触发打印,必须重置
_currentItemIndex、_currentLocationIndex和_currentPrintY,否则会从上次的进度继续打印。 - 行高计算要包含间距:避免打印内容重叠,提升可读性。
内容的提问来源于stack exchange,提问作者TC_Guy
相关产品推荐
相关产品推荐

