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

C# PrintDocument点击事件打印全页 如何实现指定页码范围打印

问题根因

你配置页码范围后仍然打印全部页面,核心是三个问题:

  1. BeginPrint事件中硬编码把起止页码都写死为1,没有读取用户实际输入的起止页码值
  2. 没有在PrintPage事件中维护当前打印页码的计数,也没有做页码范围判断,PrintDocument组件不会自动跳过范围外的内容,必须自行在绘制逻辑中控制
  3. 每次点击打印按钮都重复绑定事件,多次点击后事件会重复执行,引发逻辑异常
排查思路
  • 先检查事件绑定逻辑:打印相关事件只需要在窗体初始化时绑定一次,不要放在按钮点击事件中重复绑定
  • 检查打印参数传递:PrintRange.SomePages模式下必须保证FromPage和ToPage值合法(大于0,且起始页≤结束页),否则打印引擎会直接忽略范围配置
  • 检查打印页控制逻辑:必须单独维护当前打印页码的变量,在页码小于起始页时跳过内容绘制但保留排版计算,在页码超过结束页时立即终止打印
范围打印实现方案

1. 新增全局控制变量

在窗体类中新增页码相关的控制变量,和原有打印位置变量放在一起:

// 原有打印位置变量
private int curRow = 0;
private int curCopy = 0;
// 新增页码控制变量
private int currentPrintPage = 0; // 当前正在生成的打印页码
private int userFromPage = 1;     // 用户指定的打印起始页
private int userToPage = int.MaxValue; // 用户指定的打印结束页,默认给最大值适配全量打印

2. 修正事件绑定位置

把打印事件绑定移动到窗体构造函数中,仅绑定一次:

public YourFormClassName()
{
    InitializeComponent();
    // 仅在窗体初始化时绑定一次事件
    printDocument1.BeginPrint += printDocument1_BeginPrint;
    printDocument1.PrintPage += printDocument1_PrintPage;
}

3. 修改BeginPrint事件逻辑

去掉硬编码的页码值,初始化打印状态,读取用户输入的页码范围:

private void printDocument1_BeginPrint(object sender, System.Drawing.Printing.PrintEventArgs e)
{
    // 重置打印位置和页码计数
    curRow = 0;
    curCopy = 0;
    currentPrintPage = 0;

    if (printrange)
    {
        // 如果你用系统自带PrintDialog获取页码,直接取printDialog.PrinterSettings的FromPage/ToPage即可
        // 如果是自定义页码输入框,在这里做输入校验后给userFromPage、userToPage赋值
        // 示例校验逻辑:
        // if (userFromPage <1 || userToPage < userFromPage)
        // {
        //     MessageBox.Show("页码范围不合法");
        //     e.Cancel = true;
        //     return;
        // }

        PrintDocument printDoc = (PrintDocument)sender;
        printDoc.PrinterSettings.PrintRange = PrintRange.SomePages;
        printDoc.PrinterSettings.FromPage = userFromPage;
        printDoc.PrinterSettings.ToPage = userToPage;
        printrange = false;
    }
    else
    {
        // 全量打印时重置范围为所有页
        userFromPage = 1;
        userToPage = int.MaxValue;
    }
}

4. 修改PrintPage事件逻辑

增加页码计数和范围判断,非打印范围的页面只做排版计算不绘制内容,到达结束页后终止打印:

private void printDocument1_PrintPage(object sender, PrintPageEventArgs e)
{
    // 每进入一次PrintPage事件,当前页码+1
    currentPrintPage++;
    // 判断当前页是否需要绘制内容
    bool needDraw = currentPrintPage >= userFromPage && currentPrintPage <= userToPage;

    var curY = e.MarginBounds.Y;
    var curX = e.MarginBounds.X + 30;
    // 补充Pen的using释放,避免GDI资源泄漏
    using (Pen pen = new Pen(System.Drawing.ColorTranslator.FromHtml("#FFBD2D"), 1))
    using (var fontNormal = new Font("Arial", 10))
    using (var sf = new StringFormat())
    {
        sf.Alignment = sf.LineAlignment = StringAlignment.Center;
        int itemHeight = (int)fontNormal.GetHeight(e.Graphics);
        
        for (int row = curRow; row < dt.Rows.Count; row++)
        {
            DataRow dr = dt.Rows[row];
            if (!string.IsNullOrEmpty(dr.Field<string>(1)) &&
                int.TryParse(dr.Field<string>(4)?.ToString(), out int copies))
            {
                for (int i = curCopy; i < copies; i++)
                {
                    var imgRect = new Rectangle(curX, curY, 156, 156);
                    var labelRect = new Rectangle(
                        imgRect.X + 180 + 20,
                        imgRect.Bottom - 162,
                        itemHeight,
                        imgRect.Width);

                    // 仅在指定页码范围内才实际绘制内容
                    if (needDraw)
                    {
                        using (var qrImage = GenerateQRCODE(dr[1].ToString()))
                        {
                            e.Graphics.DrawImage(RotateImage(qrImage, -90), imgRect);
                        }

                        SizeF labelSize = e.Graphics.MeasureString(dr[1].ToString(), fontNormal);
                        using (Bitmap stringmap = new Bitmap((int)labelSize.Height + 1, (int)labelSize.Width + 1))
                        using (Graphics gbitmap = Graphics.FromImage(stringmap))
                        {
                            gbitmap.TranslateTransform(0, labelSize.Width);
                            gbitmap.RotateTransform(-90);
                            gbitmap.DrawString(dr[1].ToString(), fontNormal, Brushes.Black, new PointF(0, 0), new StringFormat());
                            e.Graphics.DrawImage(stringmap, labelRect);
                        }

                        e.Graphics.DrawLine(pen, curX, curY, curX, curY + 156);
                        e.Graphics.DrawLine(pen, curX, curY + 156, curX + 156 + 220, curY + 156);
                        e.Graphics.DrawLine(pen, curX + 156 + 220, curY + 156, curX + 156 + 220, curY);
                        e.Graphics.DrawLine(pen, curX, curY, curX + 156 + 220, curY);
                    }

                    curX = imgRect.Right + 230;
                    if (curX + imgRect.Width > e.MarginBounds.Right)
                    {
                        curX = e.MarginBounds.X + 30;
                        curY = labelRect.Bottom + 30;
                    }

                    // 页面填满时判断是否需要继续打印
                    if (curY + imgRect.Height >= e.MarginBounds.Bottom)
                    {
                        curCopy = i + 1;
                        // 当前页已经是用户指定的最后一页时,终止打印
                        e.HasMorePages = currentPrintPage < userToPage;
                        return;
                    }
                }
            }
            curRow = row + 1;
            curCopy = 0;
        }
    }

    // 所有内容遍历完成,终止打印
    e.HasMorePages = false;
    refreshprintbtn.Enabled = true;
    printdialogbtn.Enabled = true;
}

5. 简化按钮点击事件

移除点击事件中的重复绑定逻辑,仅保留打印触发逻辑:

bool printrange = false;
private void printrangebtn_Click(object sender, EventArgs e)
{
    printrange = true;
    refreshprintbtn.Enabled = false;
    printdialogbtn.Enabled = false;
    printDocument1.Print();
}
注意事项
  • 如果使用系统自带的PrintDialog控件让用户选择页码,不需要自行开发输入框,只要在对话框确认后直接读取PrintDialog.PrinterSettings.FromPage和PrintDialog.PrinterSettings.ToPage赋值即可,注意要提前把PrintDocument的PrinterSettings属性赋值给PrintDialog的对应属性,否则无法正确获取参数
  • 建议提前计算文档总页数,对用户输入的页码做合法性校验,避免输入超出总页数的页码导致打印空白页
  • 起始页之前的页面虽然不绘制内容,但必须正常执行排版位置计算,否则到达起始页时内容位置会错乱

内容的提问来源于stack exchange,提问作者Molnar Istvan

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 07:21:28