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

C# WinForms项目:实现表格布局收据打印的技术问询

我完全懂你这种 frustration——用循环输出DataGridView内容打印收据时,经常因为不同单元格内容长度不一样,整个格式就歪歪扭扭的,完全达不到规整的表格效果。别担心,咱们换个思路:手动绘制表格的边框和单元格内容,精准控制每列的宽度和对齐方式,就能完美复刻目标收据的样式了。

解决方案:手动绘制表格式收据

核心思路是放弃单纯的循环文本输出,改用PrintDocument的PrintPage事件,手动绘制表格线、表头和每行内容,固定每列的宽度和对齐规则,确保格式统一。

完整示例代码

private void printDocument1_PrintPage(object sender, PrintPageEventArgs e)
{
    // 定义打印用的字体、画笔和画刷
    Font headerFont = new Font("Arial", 12, FontStyle.Bold);
    Font contentFont = new Font("Arial", 10);
    Pen tablePen = new Pen(Color.Black, 1);
    Brush textBrush = Brushes.Black;

    // 初始化打印位置(基于页边距)
    float marginLeft = e.MarginBounds.Left;
    float marginTop = e.MarginBounds.Top;
    float currentY = marginTop;

    // 绘制收据标题
    e.Graphics.DrawString("XX商店购物收据", headerFont, textBrush, marginLeft, currentY);
    currentY += headerFont.Height + 12; // 标题下方留空白

    // 定义各列宽度(根据你的目标样式调整数值)
    float colIdWidth = 70;    // 商品编号列
    float colNameWidth = 160; // 商品名称列
    float colPriceWidth = 90; // 单价列
    float colQtyWidth = 80;   // 数量列
    float colSubWidth = 110;  // 小计列
    float totalTableWidth = colIdWidth + colNameWidth + colPriceWidth + colQtyWidth + colSubWidth;

    // 绘制表头
    float currentX = marginLeft;
    e.Graphics.DrawString("商品编号", contentFont, textBrush, currentX, currentY);
    currentX += colIdWidth;
    e.Graphics.DrawString("商品名称", contentFont, textBrush, currentX, currentY);
    currentX += colNameWidth;
    e.Graphics.DrawString("单价", contentFont, textBrush, currentX, currentY);
    currentX += colPriceWidth;
    e.Graphics.DrawString("数量", contentFont, textBrush, currentX, currentY);
    currentX += colQtyWidth;
    e.Graphics.DrawString("小计", contentFont, textBrush, currentX, currentY);
    currentY += contentFont.Height + 6;

    // 绘制表头下方的分隔线
    e.Graphics.DrawLine(tablePen, marginLeft, currentY, marginLeft + totalTableWidth, currentY);
    currentY += 4;

    // 设置金额/数量的右对齐格式
    StringFormat rightAlign = new StringFormat();
    rightAlign.Alignment = StringAlignment.Far;

    // 遍历DataGridView行,绘制内容
    foreach (DataGridViewRow row in dataGridView1.Rows)
    {
        if (row.IsNewRow) continue; // 跳过新增空行

        // 分页检查:如果当前行超出打印区域,标记需要打印下一页
        if (currentY + contentFont.Height > e.MarginBounds.Bottom)
        {
            e.HasMorePages = true;
            return;
        }

        currentX = marginLeft;
        // 商品编号(左对齐)
        e.Graphics.DrawString(row.Cells["ProductId"].Value.ToString(), contentFont, textBrush, currentX, currentY);
        currentX += colIdWidth;
        // 商品名称(左对齐)
        e.Graphics.DrawString(row.Cells["ProductName"].Value.ToString(), contentFont, textBrush, currentX, currentY);
        currentX += colNameWidth;
        // 单价(右对齐,带货币符号)
        e.Graphics.DrawString($"¥{row.Cells["Price"].Value}", contentFont, textBrush, currentX + colPriceWidth, currentY, rightAlign);
        currentX += colPriceWidth;
        // 数量(右对齐)
        e.Graphics.DrawString(row.Cells["Quantity"].Value.ToString(), contentFont, textBrush, currentX + colQtyWidth, currentY, rightAlign);
        currentX += colQtyWidth;
        // 小计(右对齐,带货币符号)
        e.Graphics.DrawString($"¥{row.Cells["Subtotal"].Value}", contentFont, textBrush, currentX + colSubWidth, currentY, rightAlign);

        // 绘制行分隔线
        e.Graphics.DrawLine(tablePen, marginLeft, currentY + contentFont.Height, marginLeft + totalTableWidth, currentY + contentFont.Height);
        currentY += contentFont.Height + 4;
    }

    // 绘制总计行
    currentY += 10;
    e.Graphics.DrawString($"总计金额: ¥{CalculateTotal()}", headerFont, textBrush, marginLeft + colIdWidth + colNameWidth + colPriceWidth + colQtyWidth, currentY, rightAlign);

    // 手动释放资源,避免内存泄漏
    headerFont.Dispose();
    contentFont.Dispose();
    tablePen.Dispose();
    rightAlign.Dispose();
}

// 计算订单总计的辅助方法
private decimal CalculateTotal()
{
    decimal total = 0;
    foreach (DataGridViewRow row in dataGridView1.Rows)
    {
        if (!row.IsNewRow && decimal.TryParse(row.Cells["Subtotal"].Value.ToString(), out decimal subtotal))
        {
            total += subtotal;
        }
    }
    return total;
}

关键优化点说明

  • 固定列宽:提前定义每列的宽度,确保每列的位置绝对固定,不会因内容长短偏移
  • 精准对齐:用StringFormat为金额、数量设置右对齐,和常规收据的排版逻辑一致
  • 表格线绘制:通过Graphics.DrawLine手动绘制分隔线,让表格结构清晰规整
  • 分页处理:自动检测内容是否超出打印区域,实现多页打印
  • 资源管理:手动释放字体、画笔等GDI+资源,避免内存占用问题

使用方式

  1. 在WinForms窗体中添加PrintDocument控件,将其PrintPage事件绑定到上面的方法
  2. 添加预览/打印按钮,调用以下代码测试效果:
private void btnPrintPreview_Click(object sender, EventArgs e)
{
    PrintPreviewDialog previewDialog = new PrintPreviewDialog();
    previewDialog.Document = printDocument1;
    previewDialog.ShowDialog();
}

这样处理后,不管单元格内容长短,每列都会严格对齐,表格线清晰,完全能达到你想要的目标收据样式。

内容的提问来源于stack exchange,提问作者Joey Arnanzo

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.28 06:22:11