别再只调API了!手把手教你用C#的PrintDocument类搞定小票打印(附完整源码)
别再只调API了手把手教你用C#的PrintDocument类搞定小票打印附完整源码在零售、餐饮等行业的软件开发中小票打印功能几乎是标配。很多开发者习惯性地寻找第三方库或现成的报表控件却忽略了.NET Framework中强大的PrintDocument类。本文将带你从零开始用最原生的方式实现专业级的小票打印功能。1. 准备工作与环境搭建1.1 创建基础项目首先创建一个Windows Forms应用程序项目。这是最常用的桌面应用开发方式也最适合小票打印场景。在Visual Studio中// 新建Windows Forms应用 File - New - Project - Windows Forms App (.NET Framework)提示建议使用.NET Framework 4.7.2或更高版本以确保最佳的兼容性和功能支持。1.2 添加必要的UI元素在Form1的设计器中添加以下控件一个ButtonbtnPrint用于触发打印一个PrintDocument组件printDocument1一个PrintPreviewDialogprintPreviewDialog1用于预览Button NamebtnPrint Text打印小票 / PrintDocument NameprintDocument1 / PrintPreviewDialog NameprintPreviewDialog1 DocumentprintDocument1 /2. 核心打印逻辑实现2.1 理解PrintDocument的工作原理PrintDocument类是.NET中打印功能的核心它通过事件驱动的方式工作。最重要的两个事件是BeginPrint打印开始前触发PrintPage每页打印内容时触发我们需要重点关注PrintPage事件在这里完成所有绘图逻辑。2.2 实现基本打印功能首先为printDocument1添加PrintPage事件处理private void printDocument1_PrintPage(object sender, PrintPageEventArgs e) { // 获取Graphics对象这是我们的画布 Graphics g e.Graphics; // 设置字体 Font titleFont new Font(宋体, 14, FontStyle.Bold); Font normalFont new Font(宋体, 10); // 绘制标题 g.DrawString(某某商店销售小票, titleFont, Brushes.Black, 100, 20); // 绘制分隔线 g.DrawLine(Pens.Black, 50, 50, 300, 50); }2.3 动态构建小票内容小票内容通常是动态生成的我们可以使用StringBuilder来构建private string BuildReceiptContent() { StringBuilder sb new StringBuilder(); sb.AppendLine(订单号: DateTime.Now.ToString(yyyyMMddHHmmss)); sb.AppendLine(收银员: 张三); sb.AppendLine(-----------------------------); sb.AppendLine(商品名称 数量 单价 金额); sb.AppendLine(-----------------------------); // 模拟商品数据 var products new ListProduct { new Product { Name 可口可乐, Quantity 2, Price 3.5m }, new Product { Name 薯片, Quantity 1, Price 8.0m } }; foreach (var p in products) { sb.AppendLine(${p.Name.PadRight(10)} {p.Quantity.ToString().PadRight(5)} {p.Price.ToString(0.00).PadRight(7)} {(p.Quantity * p.Price).ToString(0.00)}); } sb.AppendLine(-----------------------------); sb.AppendLine(总计: products.Sum(p p.Quantity * p.Price).ToString(0.00)); return sb.ToString(); }3. 高级排版技巧3.1 精确控制文本位置小票打印需要精确控制每行文本的位置。我们可以使用Graphics.MeasureString方法计算文本尺寸private void PrintReceiptContent(Graphics g, string content) { Font font new Font(宋体, 10); float yPos 60; // 起始Y坐标 float leftMargin 50; // 左边距 foreach (var line in content.Split(\n)) { g.DrawString(line, font, Brushes.Black, leftMargin, yPos); yPos font.GetHeight() 2; // 行间距 } }3.2 处理多列对齐商品列表通常需要多列对齐显示。我们可以使用String.Format或插值字符串配合固定宽度// 更精确的列对齐方式 string format {0,-15}{1,5}{2,10}{3,10}; sb.AppendLine(string.Format(format, 商品名称, 数量, 单价, 金额)); foreach (var p in products) { sb.AppendLine(string.Format(format, p.Name, p.Quantity, p.Price.ToString(0.00), (p.Quantity * p.Price).ToString(0.00))); }3.3 添加公司LOGO如果需要打印公司LOGO可以这样实现private void PrintLogo(Graphics g) { if (File.Exists(logo.bmp)) { Image logo Image.FromFile(logo.bmp); g.DrawImage(logo, 50, 10, 100, 30); } }4. 完整实现与优化4.1 完整的PrintPage事件处理将前面所有功能整合private void printDocument1_PrintPage(object sender, PrintPageEventArgs e) { Graphics g e.Graphics; // 打印LOGO PrintLogo(g); // 打印标题 Font titleFont new Font(宋体, 14, FontStyle.Bold); g.DrawString(某某商店销售小票, titleFont, Brushes.Black, 100, 50); // 打印内容 string content BuildReceiptContent(); PrintReceiptContent(g, content); // 打印页脚 Font footerFont new Font(宋体, 8); g.DrawString(谢谢惠顾欢迎再次光临, footerFont, Brushes.Black, 100, 400); g.DrawString(DateTime.Now.ToString(yyyy-MM-dd HH:mm:ss), footerFont, Brushes.Black, 100, 420); }4.2 添加打印预览功能在打印按钮的点击事件中private void btnPrint_Click(object sender, EventArgs e) { // 预览 printPreviewDialog1.ShowDialog(); // 或者直接打印 // printDocument1.Print(); }4.3 处理多页打印如果内容超出一页需要设置HasMorePages属性private void printDocument1_PrintPage(object sender, PrintPageEventArgs e) { // ...打印当前页内容... // 检查是否还有更多内容要打印 if (/* 还有内容 */) { e.HasMorePages true; } else { e.HasMorePages false; } }5. 实战技巧与常见问题5.1 选择合适的纸张大小小票打印机通常使用特殊的纸张尺寸。可以在打印前设置printDocument1.DefaultPageSettings.PaperSize new PaperSize(Custom, 300, 500);5.2 处理打印机设置允许用户选择打印机和设置打印参数PrintDialog printDialog new PrintDialog(); printDialog.Document printDocument1; if (printDialog.ShowDialog() DialogResult.OK) { printDocument1.Print(); }5.3 性能优化技巧复用Font和Brush对象避免频繁创建对于复杂的小票考虑先渲染到Bitmap再打印使用StringBuilder构建大文本内容// 复用对象示例 Font normalFont new Font(宋体, 10); SolidBrush blackBrush new SolidBrush(Color.Black); try { // 使用这些对象进行绘制... } finally { normalFont.Dispose(); blackBrush.Dispose(); }5.4 处理打印异常打印过程中可能会出现各种异常需要妥善处理private void btnPrint_Click(object sender, EventArgs e) { try { printDocument1.Print(); } catch (InvalidPrinterException ex) { MessageBox.Show(打印机设置错误: ex.Message); } catch (Exception ex) { MessageBox.Show(打印出错: ex.Message); } }6. 完整源码示例以下是完整的Form1.cs代码using System; using System.Drawing; using System.Drawing.Printing; using System.Text; using System.Windows.Forms; namespace ReceiptPrinterDemo { public partial class Form1 : Form { public Form1() { InitializeComponent(); printDocument1.PrintPage printDocument1_PrintPage; btnPrint.Click btnPrint_Click; } private void btnPrint_Click(object sender, EventArgs e) { printPreviewDialog1.ShowDialog(); } private void printDocument1_PrintPage(object sender, PrintPageEventArgs e) { Graphics g e.Graphics; // 设置纸张大小单位百分之一英寸 printDocument1.DefaultPageSettings.PaperSize new PaperSize(Custom, 300, 500); // 打印LOGO PrintLogo(g); // 打印标题 Font titleFont new Font(宋体, 14, FontStyle.Bold); g.DrawString(某某商店销售小票, titleFont, Brushes.Black, 100, 50); // 打印内容 string content BuildReceiptContent(); PrintReceiptContent(g, content); // 打印页脚 Font footerFont new Font(宋体, 8); g.DrawString(谢谢惠顾欢迎再次光临, footerFont, Brushes.Black, 100, 400); g.DrawString(DateTime.Now.ToString(yyyy-MM-dd HH:mm:ss), footerFont, Brushes.Black, 100, 420); } private string BuildReceiptContent() { StringBuilder sb new StringBuilder(); sb.AppendLine(订单号: DateTime.Now.ToString(yyyyMMddHHmmss)); sb.AppendLine(收银员: 张三); sb.AppendLine(-----------------------------); sb.AppendLine(string.Format({0,-15}{1,5}{2,10}{3,10}, 商品名称, 数量, 单价, 金额)); sb.AppendLine(-----------------------------); var products new ListProduct { new Product { Name 可口可乐, Quantity 2, Price 3.5m }, new Product { Name 薯片, Quantity 1, Price 8.0m }, new Product { Name 矿泉水, Quantity 3, Price 2.0m } }; foreach (var p in products) { sb.AppendLine(string.Format({0,-15}{1,5}{2,10}{3,10}, p.Name, p.Quantity, p.Price.ToString(0.00), (p.Quantity * p.Price).ToString(0.00))); } sb.AppendLine(-----------------------------); sb.AppendLine(总计: products.Sum(p p.Quantity * p.Price).ToString(0.00)); return sb.ToString(); } private void PrintReceiptContent(Graphics g, string content) { Font font new Font(宋体, 10); float yPos 100; float leftMargin 50; foreach (var line in content.Split(\n)) { g.DrawString(line, font, Brushes.Black, leftMargin, yPos); yPos font.GetHeight() 2; } } private void PrintLogo(Graphics g) { if (File.Exists(logo.bmp)) { try { Image logo Image.FromFile(logo.bmp); g.DrawImage(logo, 50, 10, 100, 30); } catch { /* 忽略LOGO加载错误 */ } } } } public class Product { public string Name { get; set; } public int Quantity { get; set; } public decimal Price { get; set; } } }7. 扩展功能思路7.1 支持条形码打印许多小票需要打印条形码可以使用专门的库如ZXing.Netvar writer new BarcodeWriter { Format BarcodeFormat.CODE_128, Options new EncodingOptions { Height 50, Width 150 } }; var barcodeImage writer.Write(123456789); g.DrawImage(barcodeImage, 50, 300);7.2 多语言支持根据不同客户需求切换语言string title isChinese ? 销售小票 : Sales Receipt; g.DrawString(title, titleFont, Brushes.Black, 100, 50);7.3 打印样式配置允许用户自定义打印样式public class PrintSettings { public Font TitleFont { get; set; } new Font(宋体, 14, FontStyle.Bold); public Font ContentFont { get; set; } new Font(宋体, 10); public Color TextColor { get; set; } Color.Black; public int LeftMargin { get; set; } 50; }7.4 保存打印历史将打印内容保存到数据库或文件File.WriteAllText($Receipts/{DateTime.Now:yyyyMMddHHmmss}.txt, BuildReceiptContent());
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2614883.html
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!