news 2026/7/22 3:58:04

C# 实战:利用PrintDocument类高效实现自定义打印功能

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
C# 实战:利用PrintDocument类高效实现自定义打印功能

1. 初识PrintDocument类:打印功能的核心引擎

第一次接触C#打印功能时,我完全被各种打印对话框和设置搞晕了。直到发现了PrintDocument这个神器,才发现原来实现打印功能可以如此简单。PrintDocument就像是打印功能的中央控制器,它负责协调整个打印流程,从页面设置到内容绘制,全都由它一手包办。

记得当时我需要给一个餐饮系统添加小票打印功能,用PrintDocument只花了不到半天就搞定了。这个类位于System.Drawing.Printing命名空间,使用时需要先添加引用。最让我惊喜的是,它不仅能打印文本,还能处理图像、表格等各种复杂内容。

// 最基本的打印示例 PrintDocument pd = new PrintDocument(); pd.PrintPage += (sender, e) => { e.Graphics.DrawString("Hello World", new Font("Arial", 12), Brushes.Black, 100, 100); }; pd.Print();

这段代码虽然简单,但包含了打印的三个核心要素:创建打印文档对象、定义打印内容、执行打印命令。在实际项目中,我们通常会把它封装成一个专门的打印服务类,方便各个模块调用。

2. 打印机设置:避开那些年我踩过的坑

刚开始用PrintDocument时,我最头疼的就是打印机设置问题。明明代码写对了,但打印机就是没反应。后来才发现,90%的打印问题都出在打印机配置上,而不是代码本身。

2.1 获取可用打印机列表

在打印之前,我们首先需要知道系统中有哪些打印机可用。通过PrinterSettings.InstalledPrinters可以获取所有已安装的打印机名称。这里有个小技巧:通常我们会把打印机列表显示在下拉框中,让用户自己选择。

// 获取所有打印机名称 foreach(string printer in PrinterSettings.InstalledPrinters) { Console.WriteLine(printer); } // 设置默认打印机 PrintDocument pd = new PrintDocument(); string defaultPrinter = pd.PrinterSettings.PrinterName;

2.2 处理打印机状态问题

在实际使用中,经常会遇到打印机脱机、端口错误等问题。我建议在打印前先检查打印机状态:

  1. 检查打印机是否就绪
  2. 检查是否有未完成的打印任务阻塞队列
  3. 验证打印机端口设置是否正确
// 检查打印机状态 if(!pd.PrinterSettings.IsValid) { MessageBox.Show("打印机设置无效"); return; } if(pd.PrinterSettings.IsDefaultPrinter) { // 处理默认打印机逻辑 }

3. 页面布局设计:让打印内容完美呈现

打印内容布局是另一个需要重点关注的领域。与屏幕显示不同,打印布局需要考虑纸张大小、边距、分页等实际问题。

3.1 设置页面属性

通过DefaultPageSettings属性,我们可以控制纸张大小、方向、边距等:

// 设置页面属性 pd.DefaultPageSettings.PaperSize = new PaperSize("A4", 827, 1169); // A4纸 pd.DefaultPageSettings.Margins = new Margins(50, 50, 50, 50); // 四边距50 pd.DefaultPageSettings.Landscape = true; // 横向打印

3.2 精确计算打印位置

打印内容的位置需要精确计算。我习惯使用PrintPageEventArgs提供的MarginBounds属性来确定可打印区域:

pd.PrintPage += (sender, e) => { RectangleF bounds = e.MarginBounds; float x = bounds.Left; float y = bounds.Top; // 计算行高 float lineHeight = font.GetHeight(e.Graphics); // 打印文本 e.Graphics.DrawString("订单号: 20230001", font, Brushes.Black, x, y); y += lineHeight * 1.5f; e.Graphics.DrawString("日期: " + DateTime.Now.ToString(), font, Brushes.Black, x, y); };

4. 实战案例:打印餐饮小票

让我们通过一个完整的餐饮小票打印案例,把前面学到的知识串起来。这个例子包含了文本、表格线和汇总信息的打印。

4.1 构建打印内容

首先,我们需要准备打印内容。我通常会创建一个专门的方法来生成打印文本:

public string BuildReceiptContent() { StringBuilder sb = new StringBuilder(); sb.AppendLine(" 美味餐厅 "); sb.AppendLine("---------------------"); sb.AppendLine("订单号: " + orderNumber); sb.AppendLine("时间: " + DateTime.Now.ToString("yyyy-MM-dd HH:mm")); sb.AppendLine("---------------------"); sb.AppendLine("菜品 数量 单价 小计"); foreach(var item in orderItems) { sb.AppendLine($"{item.Name,-10} {item.Quantity,3} {item.Price,6:C} {item.Total,6:C}"); } sb.AppendLine("---------------------"); sb.AppendLine($"总计: {totalAmount:C}"); sb.AppendLine("谢谢惠顾,欢迎下次光临"); return sb.ToString(); }

4.2 实现PrintPage事件

接下来是核心的打印逻辑。这里我们不仅要打印文本,还要绘制表格线:

private void PrintReceipt(object sender, PrintPageEventArgs e) { Graphics g = e.Graphics; Font titleFont = new Font("黑体", 14, FontStyle.Bold); Font contentFont = new Font("宋体", 10); Font footerFont = new Font("宋体", 8); float x = e.MarginBounds.Left; float y = e.MarginBounds.Top; // 打印标题 g.DrawString("美味餐厅", titleFont, Brushes.Black, x + (e.MarginBounds.Width - g.MeasureString("美味餐厅", titleFont).Width)/2, y); y += titleFont.GetHeight(g) * 1.5f; // 打印分隔线 g.DrawLine(Pens.Black, x, y, x + e.MarginBounds.Width, y); y += 10; // 打印订单信息 string[] lines = BuildReceiptContent().Split('\n'); foreach(string line in lines) { g.DrawString(line, contentFont, Brushes.Black, x, y); y += contentFont.GetHeight(g); } // 处理分页 e.HasMorePages = false; // 本例只有一页 }

5. 高级技巧:打印预览与多页处理

对于复杂的打印需求,我们还需要考虑打印预览和多页打印的功能。

5.1 实现打印预览

打印预览可以大大提升用户体验,避免浪费纸张:

// 打印预览 PrintPreviewDialog preview = new PrintPreviewDialog(); preview.Document = printDocument; preview.ShowDialog();

5.2 处理多页打印

当内容超过一页时,需要使用HasMorePages属性来控制分页:

private List<string> printLines; private int currentLine; private void PrintMultiPage(object sender, PrintPageEventArgs e) { Graphics g = e.Graphics; Font font = new Font("宋体", 10); float y = e.MarginBounds.Top; while(currentLine < printLines.Count) { string line = printLines[currentLine]; g.DrawString(line, font, Brushes.Black, e.MarginBounds.Left, y); y += font.GetHeight(g); currentLine++; if(y >= e.MarginBounds.Bottom) { e.HasMorePages = currentLine < printLines.Count; return; } } e.HasMorePages = false; currentLine = 0; // 重置计数器 }

6. 性能优化与异常处理

在实际项目中,打印功能还需要考虑性能和稳定性问题。

6.1 资源释放

打印完成后,一定要记得释放资源:

try { printDocument.Print(); } catch(InvalidPrinterException ex) { MessageBox.Show("打印机不可用: " + ex.Message); } finally { printDocument.Dispose(); }

6.2 异步打印

大量打印任务可能会阻塞UI线程,可以考虑使用异步打印:

// 异步打印 printDocument.PrintController = new StandardPrintController(); printDocument.BeginPrint += (s, e) => { /* 打印前准备 */ }; printDocument.EndPrint += (s, e) => { /* 打印后清理 */ }; // 在新线程中打印 Task.Run(() => printDocument.Print());

7. 常见问题解决方案

在多年的开发中,我总结了一些常见问题的解决方法:

  1. 打印内容偏移:检查打印机驱动设置中的页边距,确保与代码设置一致
  2. 中文乱码:使用支持中文的字体,如"宋体"、"微软雅黑"
  3. 打印速度慢:减少不必要的绘图操作,使用PrintController优化
  4. 图片模糊:确保图片分辨率足够高,至少300dpi
// 高质量打印设置 printDocument.DefaultPageSettings.PrinterResolution = new PrinterResolution { Kind = PrinterResolutionKind.High };

掌握了这些技巧后,你会发现用C#实现打印功能其实并不复杂。关键是要理解PrintDocument的工作原理,并在实际项目中不断实践和优化。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/19 16:57:33

通义千问3-Embedding-4B快速部署:Docker镜像使用教程

通义千问3-Embedding-4B快速部署&#xff1a;Docker镜像使用教程 你是否试过为一个知识库系统选型向量模型&#xff0c;却在显存、速度、多语言支持和长文本处理之间反复纠结&#xff1f;Qwen3-Embedding-4B 就是那个“不用妥协”的答案——它不是参数堆出来的庞然大物&#x…

作者头像 李华
网站建设 2026/7/19 19:38:01

Clawdbot+Qwen3:32B部署教程:解决Web端流式输出卡顿与断连问题

ClawdbotQwen3:32B部署教程&#xff1a;解决Web端流式输出卡顿与断连问题 1. 为什么你会遇到流式输出卡顿和断连&#xff1f; 你是不是也这样&#xff1a;在Web聊天界面输入问题后&#xff0c;Qwen3:32B明明已经开始响应&#xff0c;但文字一行行蹦出来特别慢&#xff0c;中间…

作者头像 李华
网站建设 2026/7/19 19:04:02

Qwen3-Reranker-0.6B完整指南:从test.py源码解析到生产级API封装

Qwen3-Reranker-0.6B完整指南&#xff1a;从test.py源码解析到生产级API封装 1. 为什么你需要一个轻量但靠谱的重排序模型 在RAG系统里&#xff0c;检索器&#xff08;比如BM25或向量数据库&#xff09;往往能捞出十几甚至上百个候选文档&#xff0c;但真正和用户问题高度相关…

作者头像 李华
网站建设 2026/7/21 19:25:49

Qwen3-VL-8B Web系统保姆级教程:代理服务器日志proxy.log关键字段解读

Qwen3-VL-8B Web系统保姆级教程&#xff1a;代理服务器日志proxy.log关键字段解读 1. 为什么你需要读懂proxy.log 你已经成功启动了Qwen3-VL-8B聊天系统&#xff0c;浏览器里对话流畅、响应迅速——但某天突然发现用户反馈“发送消息后卡住5秒才回复”&#xff0c;或者“上传…

作者头像 李华
网站建设 2026/7/20 21:44:07

人脸识别OOD模型部署案例:GPU显存555MB约束下的高并发压测结果

人脸识别OOD模型部署案例&#xff1a;GPU显存555MB约束下的高并发压测结果 1. 什么是人脸识别OOD模型&#xff1f; 你可能已经用过不少人脸识别系统——刷门禁、打卡考勤、手机解锁。但有没有遇到过这些情况&#xff1a; 光线太暗&#xff0c;系统说“检测不到人脸”&#x…

作者头像 李华
网站建设 2026/7/19 16:28:29

输出文件在哪找?教你快速定位生成的卡通图片

输出文件在哪找&#xff1f;教你快速定位生成的卡通图片 你刚用「unet person image cartoon compound人像卡通化」镜像把一张自拍照变成了萌趣十足的卡通头像&#xff0c;点击“下载结果”按钮后却没看到文件弹出&#xff1f;或者批量处理完20张照片&#xff0c;想手动检查某…

作者头像 李华