You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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;
        }
    }
}

关键细节解释

  1. 自定义纸张尺寸:热敏纸通常是80mm或58mm宽,所以我们需要把毫米转换成WPF的设备无关像素(因为WPF默认是96DPI),用MillimetersToPixels方法来转换。
  2. PrintVisual方法:这个方法直接打印Visual元素,所以我们的收据布局是什么样,打印出来就是什么样,完全所见即所得。
  3. 分页处理:如果收据内容很长(比如商品很多),默认PrintVisual会按纸张高度自动分页,但如果你需要更精细的分页控制,可以自定义DocumentPaginator,把内容分成多个Visual对象,每个对应一页。
  4. 预览功能:PrintDialog本身没有预览界面,但你可以把receiptContainer渲染成图片,或者放到一个窗口里显示,模拟预览效果。
额外技巧
  • 打印前可以把receiptContainer的背景设为白色,避免透明背景导致打印内容丢失。
  • 如果需要打印多份,可以在PrintDialog的PrintTicket里设置Copies属性。
  • 测试打印时,可以先选择“Microsoft Print to PDF”打印机,生成PDF文件查看效果,避免浪费纸张。

内容的提问来源于stack exchange,提问作者Ali Hamza

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.25 08:06:59