-
C#的打印功能——订单报表生成与打印
第二部分:桌面应用开发
10. 打印功能——订单报表生成与打印
实例介绍
之前做电商后台时,财务每月要打印几百份订单报表,每次都得导出Excel再调整列宽、加页眉页脚,折腾半天还容易格式错乱。后来用WPF的FlowDocument做了个报表生成工具:直接把订单数据排成规范表格,带公司logo、页眉(订单报表+日期)、页脚(页码),点一下就能预览或打印,财务说效率至少提升了三倍。这节就带你给电商后台加订单报表打印功能,彻底解vb.net教程C#教程python教程SQL教程access 2010教程决手动排版的痛点。
需求分析
订单报表打印要解决“高效生成规范报表并打印”的问题,具体需求如下:
1.报表内容:包含订单号、客户名称、下单日期、订单金额、支付状态等核心字段;
2.样式规范:带公司logo、页眉(报表标题+打印日期)、页脚(页码/总页数)、表格边框、交替行颜色;
3.交互功能:打印预览(调整参数)、直接打印、导出PDF(用于存档);
4.批量处理:支持选择多个订单批量打印,自动分页;
5.性能优化:大数据量(比如1000+订单)时自动分页,避免内存溢出;
6.参数定制:允许设置纸张大小(A4/A5)、方向(纵向/横向)、边距;
7.错误处理:打印失败时提示(比如打印机未连接),无数据时禁止打印。
代码实现
前置条件:.NET 6+、WPF;延续电商后台场景,定义Order模型和OrderViewModel;需安装NuGet包 iTextSharp(用于PDF导出,可选)或使用WPF自带的XPS转PDF。
场景1:基础打印(直接打印订单列表)
用FlowDocument生成订单报表,通过PrintDialog直接打印。
步骤1:定义Order模型和ViewModel
csharp
// Order模型:订单数据
public class Order
{
public string OrderId { get; set; } // 订单号
public string CustomerName { get; set; } // 客户名称
public DateTime OrderDate { get; set; } // 下单日期
public decimal TotalAmount { get; set; } // 订单金额
public string PaymentStatus { get; set; } // 支付状态(已支付/待支付)
}
// OrderViewModel:订单数据逻辑
public partial class OrderViewModel : ObservableObject
{
[ObservableProperty]
private ObservableCollection<Order> _orderList = new();
public OrderViewModel()
{
// 模拟订单数据
OrderList.Add(new Order { OrderId = "ORD20240001", CustomerName = "张三", OrderDate = DateTime.Now.AddDays(-5), TotalAmount = 199.99m, PaymentStatus = "已支付" });
OrderList.Add(new Order { OrderId = "ORD20240002", CustomerName = "李四", OrderDate = DateTime.Now.AddDays(-3), TotalAmount = 299.50m, PaymentStatus = "已支付" });
OrderList.Add(new Order { OrderId = "ORD20240003", CustomerName = "王五", OrderDate = DateTime.Now.AddDays(-1), TotalAmount = 450.00m, PaymentStatus = "待支付" });
}
}
步骤2:MainWindow.xaml添加打印按钮
xml
<Button Content="打印订单报表" Click="PrintOrderReport_Click" Margin="10" Style="{StaticResource PrimaryButtonStyle}"/>
步骤3:MainWindow.xaml.cs实现打印逻辑
csharp
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Media;
private void PrintOrderReport_Click(object sender, RoutedEventArgs e)
{
// 1. 创建打印对话框
var printDialog = new PrintDialog();
// 2. 设置默认打印参数(A4纸,纵向)
printDialog.PrintTicket.PageMediaSize = new PageMediaSize(PageMediaSizeName.A4);
printDialog.PrintTicket.PageOrientation = PageOrientation.Portrait;
// 3. 显示打印对话框(用户确认后执行打印)
if (printDialog.ShowDialog() == true)
{
// 4. 生成订单报表(FlowDocument)
var report = CreateOrderReport();
// 5. 打印报表
printDialog.PrintDocument(((IDocumentPaginatorSource)report).DocumentPaginator, "订单报表打印");
}
}
// 生成订单报表(FlowDocument)
private FlowDocument CreateOrderReport()
{
var viewModel = DataContext as OrderViewModel;
if (viewModel == null || viewModel.OrderList.Count == 0)
{
MessageBox.Show("无订单数据可打印!", "提示", MessageBoxButton.OK, MessageBoxImage.Information);
return new FlowDocument();
}
// 1. 初始化FlowDocument(A4纸,边距2cm)
var doc = new FlowDocument
{
PageWidth = 816, // A4宽度(1英寸=96像素,21cm≈816像素)
PageHeight = 1056, // A4高度(29.7cm≈1056像素)
PagePadding = new Thickness(72), // 边距1英寸(72像素)
FontFamily = new FontFamily("微软雅黑"),
FontSize = 12
};
// 2. 添加页眉(报表标题+打印日期)
var header = new Paragraph(new Run($"订单报表 - 打印日期:{DateTime.Now:yyyy-MM-dd HH:mm}"))
{
FontSize = 16,
FontWeight = FontWeights.Bold,
HorizontalAlignment = HorizontalAlignment.Center,
Margin = new Thickness(0, 0, 0, 20)
};
doc.Blocks.Add(header);
// 3. 添加订单表格
var orderTable = new Table();
orderTable.CellSpacing = 0; // 单元格间距为0
orderTable.BorderThickness = new Thickness(1);
orderTable.BorderBrush = Brushes.Black;
// 3.1 定义列(订单号、客户、日期、金额、状态)
orderTable.Columns.Add(new TableColumn { Width = new GridLength(150) });
orderTable.Columns.Add(new TableColumn { Width = new GridLength(120) });
orderTable.Columns.Add(new TableColumn { Width = new GridLength(150) });
orderTable.Columns.Add(new TableColumn { Width = new GridLength(120) });
orderTable.Columns.Add(new TableColumn { Width = new GridLength(100) });
// 3.2 添加表头行
var headerRow = new TableRow();
headerRow.Background = Brushes.LightGray;
headerRow.FontWeight = FontWeights.Bold;
// 表头单元格
headerRow.Cells.Add(CreateTableCell("订单号"));
headerRow.Cells.Add(CreateTableCell("客户名称"));
headerRow.Cells.Add(CreateTableCell("下单日期"));
headerRow.Cells.Add(CreateTableCell("订单金额(元)"));
headerRow.Cells.Add(CreateTableCell("支付状态"));
// 添加表头到表格
var headerGroup = new TableRowGroup();
headerGroup.RowGroups.Add(new TableRowGroup());
headerGroup.Rows.Add(headerRow);
orderTable.RowGroups.Add(headerGroup);
// 3.3 添加订单数据行
var dataGroup = new TableRowGroup();
int rowIndex = 0;
foreach (var order in viewModel.OrderList)
{
var row = new TableRow();
// 交替行颜色(偶数行浅灰)
if (rowIndex % 2 == 1)
row.Background = new SolidColorBrush(Color.FromArgb(20, 0, 0, 0));
// 数据单元格
row.Cells.Add(CreateTableCell(order.OrderId));
row.Cells.Add(CreateTableCell(order.CustomerName));
row.Cells.Add(CreateTableCell(order.OrderDate.ToString("yyyy-MM-dd")));
row.Cells.Add(CreateTableCell(order.TotalAmount.ToString("N2")));
row.Cells.Add(CreateTableCell(order.PaymentStatus));
dataGroup.Rows.Add(row);
rowIndex++;
}
orderTable.RowGroups.Add(dataGroup);
// 4. 添加表格到文档
doc.Blocks.Add(orderTable);
return doc;
}
// 辅助方法:创建表格单元格
private TableCell CreateTableCell(string text)
{
return new TableCell(new Paragraph(new Run(text))
{
Padding = new Thickness(5),
Margin = new Thickness(0)
})
{
BorderThickness = new Thickness(0.5),
BorderBrush = Brushes.Black
};
}
场景2:打印预览
添加打印预览功能,用户可在打印前查看报表样式和分页。
步骤1:MainWindow.xaml添加预览按钮
xml
<Button Content="打印预览" Click="PrintPreview_Click" Margin="10" Style="{StaticResource SecondaryButtonStyle}"/>
步骤2:MainWindow.xaml.cs实现预览逻辑
csharp
private void PrintPreview_Click(object sender, RoutedEventArgs e)
{
var report = CreateOrderReport();
if (report.Blocks.Count == 0) return;
// 1. 创建打印预览窗口
var previewWindow = new Window
{
Title = "订单报表预览",
Width = 900,
Height = 1000,
WindowStartupLocation = WindowStartupLocation.CenterOwner,
Owner = this
};
// 2. 添加DocumentViewer(显示FlowDocument)
var viewer = new DocumentViewer
{
Document = report
};
previewWindow.Content = viewer;
// 3. 显示预览窗口
previewWindow.ShowDialog();
}
场景3:导出PDF报表
将订单报表导出为PDF文件(用于存档或邮件发送)。
步骤1:安装NuGet包
安装 iTextSharp(版本5.5.13.3)或 PdfSharp(二选一,这里用iTextSharp)。
步骤2:添加导出PDF按钮
xml
<Button Content="导出PDF报表" Click="ExportPdf_Click" Margin="10" Style="{StaticResource TertiaryButtonStyle}"/>
步骤3:实现导出逻辑
csharp
using iTextSharp.text;
using iTextSharp.text.pdf;
using System.IO;
using System.Windows;
private void ExportPdf_Click(object sender, RoutedEventArgs e)
{
var viewModel = DataContext as OrderViewModel;
if (viewModel == null || viewModel.OrderList.Count == 0)
{
MessageBox.Show("无订单数据可导出!", "提示", MessageBoxButton.OK, MessageBoxImage.Information);
return;
}
// 1. 显示保存文件对话框
var saveDialog = new SaveFileDialog
{
Filter = "PDF文件|*.pdf",
FileName = $"订单报表_{DateTime.Now:yyyyMMdd}.pdf",
InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.Desktop)
};
if (saveDialog.ShowDialog() == true)
{
// 2. 生成PDF文件
using (var fs = new FileStream(saveDialog.FileName, FileMode.Create))
{
// 初始化PDF文档(A4)
var document = new Document(PageSize.A4, 72, 72, 72, 72); // 边距1英寸
PdfWriter.GetInstance(document, fs);
document.Open();
// 添加标题
var titleFont = FontFactory.GetFont("微软雅黑", 16, Font.BOLD);
var title = new Paragraph($"订单报表 - 导出日期:{DateTime.Now:yyyy-MM-dd HH:mm}", titleFont)
{
Alignment = Element.ALIGN_CENTER,
SpacingAfter = 20
};
document.Add(title);
// 添加表格
var table = new PdfPTable(5); // 5列
table.WidthPercentage = 100; // 占满页面宽度
table.SetWidths(new float[] { 20, 15, 20, 15, 15 }); // 列宽比例
// 表头样式
var headerFont = FontFactory.GetFont("微软雅黑", 12, Font.BOLD);
var headerCellStyle = new PdfPCell();
headerCellStyle.BackgroundColor = BaseColor.LIGHT_GRAY;
headerCellStyle.HorizontalAlignment = Element.ALIGN_CENTER;
headerCellStyle.Padding = 5;
// 添加表头
table.AddCell(new PdfPCell(new Phrase("订单号", headerFont)) { BackgroundColor = BaseColor.LIGHT_GRAY, HorizontalAlignment = Element.ALIGN_CENTER, Padding =5 });
table.AddCell(new PdfPCell(new Phrase("客户名称", headerFont)) { BackgroundColor = BaseColor.LIGHT_GRAY, HorizontalAlignment = Element.ALIGN_CENTER, Padding =5 });
table.AddCell(new PdfPCell(new Phrase("下单日期", headerFont)) { BackgroundColor = BaseColor.LIGHT_GRAY, HorizontalAlignment = Element.ALIGN_CENTER, Padding =5 });
table.AddCell(new PdfPCell(new Phrase("订单金额", headerFont)) { BackgroundColor = BaseColor.LIGHT_GRAY, HorizontalAlignment = Element.ALIGN_CENTER, Padding =5 });
table.AddCell(new PdfPCell(new Phrase("支付状态", headerFont)) { BackgroundColor = BaseColor.LIGHT_GRAY, HorizontalAlignment = Element.ALIGN_CENTER, Padding =5 });
// 添加数据行
var dataFont = FontFactory.GetFont("微软雅黑", 10);
int rowIndex =0;
foreach (var order in viewModel.OrderList)
{
var cellStyle = new PdfPCell();
cellStyle.Padding =5;
cellStyle.HorizontalAlignment = Element.ALIGN_CENTER;
// 交替行颜色
if (rowIndex%2 ==1)
cellStyle.BackgroundColor = new BaseColor(240,240,240);
table.AddCell(new PdfPCell(new Phrase(order.OrderId, dataFont)) { Padding=5, HorizontalAlignment=Element.ALIGN_CENTER, BackgroundColor= rowIndex%2==1?new BaseColor(240,240,240):BaseColor.WHITE });
table.AddCell(new PdfPCell(new Phrase(order.CustomerName, dataFont)) { Padding=5, HorizontalAlignment=Element.ALIGN_CENTER, BackgroundColor= rowIndex%2==1?new BaseColor(240,240,240):BaseColor.WHITE });
table.AddCell(new PdfPCell(new Phrase(order.OrderDate.ToString("yyyy-MM-dd"), dataFont)) { Padding=5, HorizontalAlignment=Element.ALIGN_CENTER, BackgroundColor= rowIndex%2==1?new BaseColor(240,240,240):BaseColor.WHITE });
table.AddCell(new PdfPCell(new Phrase(order.TotalAmount.ToString("N2"), dataFont)) { Padding=5, HorizontalAlignment=Element.ALIGN_CENTER, BackgroundColor= rowIndex%2==1?new BaseColor(240,240,240):BaseColor.WHITE });
table.AddCell(new PdfPCell(new Phrase(order.PaymentStatus, dataFont)) { Padding=5, HorizontalAlignment=Element.ALIGN_CENTER, BackgroundColor= rowIndex%2==1?new BaseColor(240,240,240):BaseColor.WHITE });
rowIndex++;
}
document.Add(table);
document.Close();
}
MessageBox.Show($"PDF报表已导出到:
{saveDialog.FileName}", "导出成功", MessageBoxButton.OK, MessageBoxImage.Information);
}
}
逐行讲解
场景1:基础打印核心代码
1.PrintDialog初始化:创建PrintDialog实例,设置默认纸张大小(A4)和方向(纵向);
2.ShowDialog:显示打印对话框,用户选择打印机和参数后返回true;
3.FlowDocument创建:FlowDocument是WPF中用于流式排版的文档,支持自动分页、样式定制;
1.PageWidth/PageHeight:设置A4纸的像素大小(1英寸=96像素,A4=21cm×29.7cm≈816×1056像素);
2.PagePadding:设置页面边距(72像素=1英寸);
4.Table创建:Table用于展示表格数据,定义列宽、表头、数据行;
1.CellSpacing:单元格间距设为0,让边框连续;
2.交替行颜色:通过rowIndex%2判断,偶数行设浅灰色,提升可读性;
5.PrintDocument:将FlowDocument转为DocumentPaginator,传递给PrintDialog打印。
场景2:打印预览
1.DocumentViewer:WPF自带的文档预览控件,直接绑定FlowDocument即可显示;
2.预览窗口:创建独立窗口放置DocumentViewer,用户可缩放、翻页查看报表样式;
场景3:PDF导出(iTextSharp)
1.iTextSharp初始化:创建Document实例,设置A4纸和边距;
2.PdfWriter:将Document内容写入文件流;
3.PdfPTable:iTextSharp中的表格控件,设置列宽比例、表头样式、数据行;
4.交替行颜色:通过rowIndex判断,设置单元格背景色;
基础知识拓展
- FlowDocument vs FixedDocument
| 特性 | FlowDocument | FixedDocument |
|---|---|---|
| 排版方式 | 流式排版(自适应窗口大小) | 固定布局(内容位置固定,类似PDF) |
| 分页 | 自动分页(根据页面大小) | 手动分页(需显式添加PageContent) |
| 适用场景 | 长文本、报表、文档(需自适应) | 固定格式文档(如PDF、海报、证书) |
| 打印支持 | 支持(需转为DocumentPaginator) | 支持(天生分页) |
| 编辑性 | 支持(可添加TextBox等交互控件) | 不支持(静态内容) |
-
打印参数定制
纸张方向:printDialog.PrintTicket.PageOrientation = PageOrientation.Landscape(横向);
自定义纸张大小:new PageMediaSize(PageMediaSizeName.Custom, width, height);
边距设置:FlowDocument的PagePadding或PrintDialog的PrintTicket.PageMediaSize.Margin;
打印机选择:printDialog.PrintQueue可指定默认打印机,或通过LocalPrintServer.GetPrintQueues()获取所有打印机列表。 -
批量打印优化
异步打印:大数据量时用Task.Run(() => printDialog.PrintDocument(...))避免UI阻塞;
分页加载:将订单数据分批次生成FlowDocument,每批次打印一页;
打印队列管理:用PrintSystemJobInfo监控打印任务状态(成功/失败/取消)。 -
PDF导出的其他方法
WPF自带XPS转PDF:先将FlowDocument保存为XPS文件,再用第三方工具(如Ghostscript)转为PDF;
PdfSharp:开源PDF库,支持直接生成PDF,语法更接近WPF;
Syncfusion PDF:商业库,功能强大(支持表格、图片、页眉页脚),但需付费。
总结
订单报表打印的核心是报表生成和打印交互:
1.报表生成:用FlowDocument实现灵活排版,支持表格、样式、分页;
2.打印交互:PrintDialog负责用户选择打印机和参数,DocumentViewer实现预览;
3.PDF导出:用iTextSharp或PdfSharp生成PDF文件,满足存档需求;
4.性能优化:大数据量时分页、异步打印,避免UI阻塞。
掌握这些技巧后,你可以轻松实现各种复杂报表的打印功能,从电商订单到财务报表,再到物流单据,都能高效处理!
本站原创,转载请注明出处:https://www.xin3721.com/ArticlecSharp/c49469.html










