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+资源,避免内存占用问题
使用方式
- 在WinForms窗体中添加
PrintDocument控件,将其PrintPage事件绑定到上面的方法 - 添加预览/打印按钮,调用以下代码测试效果:
private void btnPrintPreview_Click(object sender, EventArgs e) { PrintPreviewDialog previewDialog = new PrintPreviewDialog(); previewDialog.Document = printDocument1; previewDialog.ShowDialog(); }
这样处理后,不管单元格内容长短,每列都会严格对齐,表格线清晰,完全能达到你想要的目标收据样式。
内容的提问来源于stack exchange,提问作者Joey Arnanzo
相关产品推荐
相关产品推荐

