You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.09 08:50:29