如何在WPF中设计并打印收据?仅用PrintDialog,不使用Report Viewer或Crystal Report
嘿,我来给你一步步拆解怎么在WPF里实现收据设计和打印,完全不用Report Viewer或者Crystal Report,就用原生的PrintDialog搞定。其实核心就是先把收据的UI布局做出来,再通过WPF的可视化打印能力把这个UI输出到打印机上,很直接。
第一步:设计收据的WPF布局
首先你需要用WPF的布局控件(比如Grid、StackPanel)来搭建收据的结构——毕竟收据就是固定格式的文本和数据组合,用常规的WPF控件完全能搞定。
比如我们可以做一个包含公司信息、订单详情、商品明细、总计的标准收据,这里用Grid来精确控制每一行的位置,避免排版混乱。下面是一个示例XAML:
<Window x:Class="WpfReceiptPrinter.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="收据设计" Width="300" Height="500"> <Grid x:Name="receiptContainer" Background="White" Padding="10"> <!-- 公司抬头 --> <StackPanel HorizontalAlignment="Center" Margin="0,0,0,20"> <TextBlock Text="XX便利店" FontSize="16" FontWeight="Bold"/> <TextBlock Text="地址:XX市XX区XX路XX号" FontSize="10"/> <TextBlock Text="电话:123-45678901" FontSize="10"/> </StackPanel> <!-- 订单信息 --> <Grid Margin="0,60,0,20"> <Grid.ColumnDefinitions> <ColumnDefinition Width="Auto"/> <ColumnDefinition Width="*"/> </Grid.ColumnDefinitions> <TextBlock Grid.Column="0" Text="订单号:" FontSize="10"/> <TextBlock Grid.Column="1" Text="ORD20240520001" FontSize="10" HorizontalAlignment="Right"/> <TextBlock Grid.Column="0" Text="日期:" FontSize="10" Margin="0,10,0,0"/> <TextBlock Grid.Column="1" Text="2024-05-20 14:30" FontSize="10" HorizontalAlignment="Right" Margin="0,10,0,0"/> </Grid> <!-- 商品明细表头 --> <Grid Margin="0,120,0,0"> <Grid.ColumnDefinitions> <ColumnDefinition Width="*"/> <ColumnDefinition Width="Auto"/> <ColumnDefinition Width="Auto"/> </Grid.ColumnDefinitions> <TextBlock Grid.Column="0" Text="商品名称" FontWeight="Bold" FontSize="10"/> <TextBlock Grid.Column="1" Text="数量" FontWeight="Bold" FontSize="10" Margin="20,0,0,0"/> <TextBlock Grid.Column="2" Text="金额" FontWeight="Bold" FontSize="10" Margin="20,0,0,0"/> </Grid> <!-- 商品明细列表 --> <ItemsControl x:Name="productItems" Margin="0,150,0,0"> <ItemsControl.ItemTemplate> <DataTemplate> <Grid Margin="0,5,0,0"> <Grid.ColumnDefinitions> <ColumnDefinition Width="*"/> <ColumnDefinition Width="Auto"/> <ColumnDefinition Width="Auto"/> </Grid.ColumnDefinitions> <TextBlock Grid.Column="0" Text="{Binding Name}" FontSize="10"/> <TextBlock Grid.Column="1" Text="{Binding Qty}" FontSize="10" Margin="20,0,0,0"/> <TextBlock Grid.Column="2" Text="{Binding Amount, StringFormat='¥{0:F2}'}" FontSize="10" Margin="20,0,0,0"/> </Grid> </DataTemplate> </ItemsControl.ItemTemplate> </ItemsControl> <!-- 总计 --> <StackPanel Margin="0,350,0,0" HorizontalAlignment="Right"> <Grid Margin="0,5,0,0"> <Grid.ColumnDefinitions> <ColumnDefinition Width="Auto"/> <ColumnDefinition Width="Auto"/> </Grid.ColumnDefinitions> <TextBlock Grid.Column="0" Text="小计:" FontSize="10"/> <TextBlock Grid.Column="1" Text="¥89.50" FontSize="10" Margin="20,0,0,0"/> </Grid> <Grid Margin="0,5,0,0"> <Grid.ColumnDefinitions> <ColumnDefinition Width="Auto"/> <ColumnDefinition Width="Auto"/> </Grid.ColumnDefinitions> <TextBlock Grid.Column="0" Text="实收:" FontSize="10" FontWeight="Bold"/> <TextBlock Grid.Column="1" Text="¥100.00" FontSize="10" FontWeight="Bold" Margin="20,0,0,0"/> </Grid> <Grid Margin="0,5,0,0"> <Grid.ColumnDefinitions> <ColumnDefinition Width="Auto"/> <ColumnDefinition Width="Auto"/> </Grid.ColumnDefinitions> <TextBlock Grid.Column="0" Text="找零:" FontSize="10"/> <TextBlock Grid.Column="1" Text="¥10.50" FontSize="10" Margin="20,0,0,0"/> </Grid> </StackPanel> <!-- 底部备注 --> <TextBlock Text="感谢您的光临,欢迎再次惠顾!" HorizontalAlignment="Center" Margin="0,420,0,0" FontSize="10" Foreground="Gray"/> </Grid> </Window>
这个布局里,我们用receiptContainer作为整个收据的根容器,后面打印的时候直接打印这个Grid就行。你可以根据自己的需求调整字体、间距、内容,完全和做普通WPF页面一样。
第二步:实现打印逻辑
接下来就是用PrintDialog来完成打印操作。WPF的PrintVisual方法可以直接把一个Visual元素(比如我们的receiptContainer)打印出来,非常方便。
另外要注意:如果是用热敏纸打印(比如超市小票那种),需要设置自定义的纸张尺寸,不然会按默认A4打印,浪费纸张。
下面是C#的打印逻辑示例:
using System; using System.Windows; using System.Windows.Controls; using System.Windows.Documents; using System.Windows.Media; namespace WpfReceiptPrinter { public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); // 模拟加载商品数据 LoadSampleData(); } private void LoadSampleData() { var products = new[] { new { Name = "矿泉水", Qty = 2, Amount = 6.00 }, new { Name = "面包", Qty = 1, Amount = 8.50 }, new { Name = "方便面", Qty = 3, Amount = 15.00 }, new { Name = "火腿肠", Qty = 5, Amount = 10.00 } }; productItems.ItemsSource = products; } private void PrintButton_Click(object sender, RoutedEventArgs e) { var printDialog = new PrintDialog(); // 关键:设置自定义纸张尺寸(比如80mm宽的热敏纸) // 转换毫米为WPF的设备无关像素(96DPI) double paperWidthMm = 80; double paperHeightMm = 297; // 足够长的高度,避免分页 var customPageSize = new PageMediaSize( PageMediaSizeName.Custom, MillimetersToPixels(paperWidthMm), MillimetersToPixels(paperHeightMm)); printDialog.PrintTicket.PageMediaSize = customPageSize; // 设置打印边距(可选) printDialog.PrintTicket.PageMargins = new PageMargins(0, 0, 0, 0); if (printDialog.ShowDialog() == true) { try { // 打印整个收据容器 printDialog.PrintVisual(receiptContainer, "便利店收据"); MessageBox.Show("打印成功!"); } catch (Exception ex) { MessageBox.Show($"打印失败:{ex.Message}"); } } } // 辅助方法:毫米转WPF像素(96DPI) private double MillimetersToPixels(double mm) { return mm * 96 / 25.4; } } }
关键细节解释
- 自定义纸张尺寸:热敏纸通常是80mm或58mm宽,所以我们需要把毫米转换成WPF的设备无关像素(因为WPF默认是96DPI),用
MillimetersToPixels方法来转换。 - PrintVisual方法:这个方法直接打印Visual元素,所以我们的收据布局是什么样,打印出来就是什么样,完全所见即所得。
- 分页处理:如果收据内容很长(比如商品很多),默认
PrintVisual会按纸张高度自动分页,但如果你需要更精细的分页控制,可以自定义DocumentPaginator,把内容分成多个Visual对象,每个对应一页。 - 预览功能:
PrintDialog本身没有预览界面,但你可以把receiptContainer渲染成图片,或者放到一个窗口里显示,模拟预览效果。
额外技巧
- 打印前可以把
receiptContainer的背景设为白色,避免透明背景导致打印内容丢失。 - 如果需要打印多份,可以在
PrintDialog的PrintTicket里设置Copies属性。 - 测试打印时,可以先选择“Microsoft Print to PDF”打印机,生成PDF文件查看效果,避免浪费纸张。
内容的提问来源于stack exchange,提问作者Ali Hamza
相关产品推荐
相关产品推荐

