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

C# WinForms中PrintDocument嵌套循环打印的分页问题

解决C# WinForms库存报表内层循环分页问题

核心思路

要处理商品存放位置跨页的情况,不能一次性打印完所有位置,必须在打印每个位置前检查页面剩余空间:空间不足时触发分页,打印新页眉后继续当前商品的剩余位置。关键是要跟踪打印进度——记录当前处理到哪个商品、该商品已打印多少个位置,确保分页后能从断点继续。

具体实现步骤

  • 保存打印状态:在打印类中定义成员变量,比如currentProductIndex(当前商品索引)、currentLocationIndex(当前商品已打印的位置索引),用于记录断点。
  • 拆分打印逻辑:
    1. 在PrintPage事件中,先打印当前商品的头部信息(如果是该商品的第一页或分页后)。
    2. 循环打印当前商品的剩余位置:每次打印前计算行高,判断是否超出页边距剩余空间。
      • 空间足够:打印位置,更新currentLocationIndex和当前Y坐标。
      • 空间不足:设置e.HasMorePages = true,退出当前事件,等待下一次触发(自动分页后继续打印剩余位置)。
    3. 当前商品所有位置打印完成后,重置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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.09 12:01:12