在C# WPF中编写漂亮的收据以供热敏打印机POS打印

20

我正在尝试在我的项目中实现打印功能,但我对这种工作并不熟练。

我已经连接了我的热敏打印机并打印了相同的样品。 现在我正在尝试找到一些方法来设计我的收据,使其看起来像附带图像中的收据。

我有一些想法,但我不确定它们是否适合这种工作,其中之一是尝试在HTML中格式化我的收据,然后呈现HTML为位图(图像),然后将其打印出来,但是我已经尝试过这个方法,看起来像是有一些质量损失。

如果有人对如何使收据看起来像图片中的收据有任何其他想法,请与我分享这些信息,我将非常感激。

这是我已经做的用于打印样品的内容,我使用了graphics.DrawString进行格式化,但我认为我无法通过它完成太多工作。

public void Print()
{
    var doc = new PrintDocument();
    doc.PrintPage += new PrintPageEventHandler(ProvideContent);
    doc.Print();
}

public void ProvideContent(object sender, PrintPageEventArgs e)
{
    Graphics graphics = e.Graphics;
    Font font = new Font("Courier New", 10);

    float fontHeight = font.GetHeight();

    int startX = 0;
    int startY = 0;
    int Offset = 20;

    e.PageSettings.PaperSize.Width = 50;
    graphics.DrawString("Welcome to MSST", new Font("Courier New", 8),
                        new SolidBrush(Color.Black), startX, startY + Offset);
    Offset = Offset + 20;

    graphics.DrawString("Ticket No:" + "4525554654545",
                new Font("Courier New", 14),
                new SolidBrush(Color.Black), startX, startY + Offset);
    Offset = Offset + 20;


    graphics.DrawString("Ticket Date :" + "21/12/215",
                new Font("Courier New", 14),
                new SolidBrush(Color.Black), startX, startY + Offset);

    Offset = Offset + 20;
    String underLine = "------------------------------------------";

    graphics.DrawString(underLine, new Font("Courier New", 14),
                new SolidBrush(Color.Black), startX, startY + Offset);

    Offset = Offset + 20;
    String Grosstotal = "Total Amount to Pay = " + "2566";

    Offset = Offset + 20;
    underLine = "------------------------------------------";
    graphics.DrawString(underLine, new Font("Courier New", 14),
                new SolidBrush(Color.Black), startX, startY + Offset);
    Offset = Offset + 20;

    graphics.DrawString(Grosstotal, new Font("Courier New", 14),
                new SolidBrush(Color.Black), startX, startY + Offset);

}

在此输入图片描述


1
这个问题的答案可能需要一些时间,假设你只是调用PrintDialog.Print,你可以轻松地完成这个过程。简而言之,你需要创建自己的DocumentPaginator。重写的打印函数将为收据创建一个UserControl,并将打印控件的DataContext设置为某种ReceiptViewModel。 - Kcvin
4个回答

27

在过去,当我这样做时,我会将收据分成不同的部分,使用不同的字体或对齐方式,例如标题、正文和页脚。

我使用以下类布局来封装我的打印文本定义。 (从哪里获取字体以及如何管理其生命周期由您决定)

public class PrintText
{
    public PrintText(string text, Font font) : this(text, font, new StringFormat()) {}

    public PrintText(string text, Font font, StringFormat stringFormat)
    {
        Text = text;
        Font = font;
        StringFormat = stringFormat;
    }

    public string Text { get; set; }

    public Font Font { get; set; }

    /// <summary> Default is horizontal string formatting </summary>
    public StringFormat StringFormat { get; set; }
}

如果有使用相同字体和填充的较长文本列表时,使用字符串构建器来构建您的文本可以使生活更加轻松,这样您只需检查代码即可获得视觉效果。

如果您有静态文本,可以将它们全部组合在一起:

var sb = new StringBuilder();
sb.AppendLine("Start of receipt");
sb.AppendLine("================");
sb.AppendLine("Item 1");
sb.AppendLine("Item 2");
sb.AppendLine("================");

或者,如果数据有点动态,可以传入一个对象进行迭代并附加您格式化的文本:

private class ReceiptItem
{
    public string Name { get; set; }

    public decimal Cost { get; set; }

    public int Amount { get; set; }

    public int Discount { get; set; }

    public decimal Total { get { return Cost * Amount; } }
}
const int FIRST_COL_PAD = 20;
const int SECOND_COL_PAD = 7;
const int THIRD_COL_PAD = 20;

var sb = new StringBuilder();
sb.AppendLine("Start of receipt");
sb.AppendLine("================");

foreach (var item in receiptItems)
{
    sb.Append(item.Name.PadRight(FIRST_COL_PAD));

    var breakDown = item.Amount > 0 ? item.Amount + "x" + item.Cost : string.Empty;
    sb.Append(breakDown.PadRight(SECOND_COL_PAD));

    sb.AppendLine(string.Format("{0:0.00} A", item.Total).PadLeft(THIRD_COL_PAD));

    if (item.Discount > 0)
    {
        sb.Append(string.Format("DISCOUNT {0:D2}%", item.Discount).PadRight(FIRST_COL_PAD + SECOND_COL_PAD));
        sb.Append(string.Format("{0:0.00} A", -(item.Total / 100 * item.Discount)).PadLeft(THIRD_COL_PAD));
        sb.AppendLine();
    }
}

sb.AppendLine("================");

输出结果将如下所示:

Start of receipt
================
Joes Food           1x10      10.00 A
DISCOUNT 10%                  -1.00 A
Fun Facts           1x20      20.00 A
DISCOUNT 15%                  -3.00 A
Bag of Sand         7x40     280.00 A
================

利用之前的PrintText类,我们可以存储格式良好的字符串生成器输出。

var printText = new PrintText(sb.ToString(), new Font("Monospace Please...", 8));

最后,尝试绘制该字符串时使用它。

var layoutArea = new SizeF(AvailableWidth, 0);
SizeF stringSize = g.MeasureString(printText.Text, printText.Font, layoutArea, printText.StringFormat);

RectangleF rectf = new RectangleF(new PointF(), new SizeF(AvailableWidth, stringSize.Height));

g.DrawString(printText.Text, printText.Font, Brushes.Black, rectf, printText.StringFormat);

如果文本打印不正确,您还可以尝试一些不同的图形调整,例如:

g.SmoothingMode = SmoothingMode.AntiAlias;
g.InterpolationMode = InterpolationMode.HighQualityBicubic;
g.PixelOffsetMode = PixelOffsetMode.HighQuality;

惊人的是,能否将生成的图像打印到收据打印机上? - VAAA
1
对不起,请问我在哪里可以下载Monospace Please...字体? - VAAA
1
那不是真正的字体。我只是在说,打印收据项目时使用的字体应该是等宽字体,这样文本和数字/价格才能正确对齐。 - laurencee
当页面大小超过a4时,此解决方案总是停止打印。将页面大小设置为卷或1000厘米长并没有帮助。就像Windows打印驱动程序总是接管分页一样?在收据上添加1000个销售项目,您就会明白我的意思。 - clamchoda
嗨,最近几天我遇到了同样的问题,但是您的答案帮助我很多,让我了解如何在热敏打印机上打印收据。我只有一个问题,这个解决方案适用于任何热敏打印机吗?我是C# .net的新手,所以我的客户要求在热敏打印机上打印收据,而我对打印毫无头绪 :) - Ezaz
显示剩余4条评论

4

我设计了一个简单流畅的收据样式,希望能对你有所帮助。

public class PrintJob
{
    private PrintDocument PrintDocument;
    private Graphics graphics;
    private Order order { set; get; }
    private Shop shop { set; get; }
    private int InitialHeight = 360;
    public PrintJob(Order order, Shop shop)
    {
        this.order = order;
        this.shop = shop;
        AdjustHeight();
    }
    private void AdjustHeight()
    {
        var capacity = 5 * order.ItemTransactions.Capacity;
        InitialHeight += capacity;

        capacity = 5 * order.DealTransactions.Capacity;
        InitialHeight += capacity;
    }
    public void Print(string printername)
    {
        PrintDocument = new PrintDocument();
        PrintDocument.PrinterSettings.PrinterName = printername;

        PrintDocument.PrintPage += new PrintPageEventHandler(FormatPage);
        PrintDocument.Print();
    }
    void DrawAtStart(string text, int Offset)
    {
        int startX = 10;
        int startY = 5;
        Font minifont = new Font("Arial", 5);

        graphics.DrawString(text, minifont,
                 new SolidBrush(Color.Black), startX + 5, startY + Offset);
    }
    void InsertItem(string key, string value, int Offset)
    {
        Font minifont = new Font("Arial", 5);
        int startX = 10;
        int startY = 5;

        graphics.DrawString(key, minifont,
                     new SolidBrush(Color.Black), startX + 5, startY + Offset);

        graphics.DrawString(value, minifont,
                 new SolidBrush(Color.Black), startX + 130, startY + Offset);
    }
    void InsertHeaderStyleItem(string key, string value, int Offset)
    {
        int startX = 10;
        int startY = 5;
        Font itemfont = new Font("Arial", 6, FontStyle.Bold);

        graphics.DrawString(key, itemfont,
                     new SolidBrush(Color.Black), startX + 5, startY + Offset);

        graphics.DrawString(value, itemfont,
                 new SolidBrush(Color.Black), startX + 130, startY + Offset);
    }
    void DrawLine(string text, Font font, int Offset, int xOffset)
    {
        int startX = 10;
        int startY = 5;
        graphics.DrawString(text, font,
                 new SolidBrush(Color.Black), startX + xOffset, startY + Offset);
    }
    void DrawSimpleString(string text, Font font, int Offset, int xOffset)
    {
        int startX = 10;
        int startY = 5;
        graphics.DrawString(text, font,
                 new SolidBrush(Color.Black), startX + xOffset, startY + Offset);
    }
    private void FormatPage(object sender, PrintPageEventArgs e)
    {
        graphics = e.Graphics;
        Font minifont = new Font("Arial", 5);
        Font itemfont = new Font("Arial", 6);
        Font smallfont = new Font("Arial", 8);
        Font mediumfont = new Font("Arial", 10);
        Font largefont = new Font("Arial", 12);
        int Offset = 10;
        int smallinc = 10, mediuminc = 12, largeinc = 15;

        //Image image = Resources.logo;
        //e.Graphics.DrawImage(image, startX + 50, startY + Offset, 100, 30);

        //graphics.DrawString("Welcome to HOT AND CRISPY", smallfont,
        //      new SolidBrush(Color.Black), startX + 22, startY + Offset);

        Offset = Offset + largeinc + 10;

        String underLine = "-------------------------------------";
        DrawLine(underLine, largefont, Offset, 0);

        Offset = Offset + mediuminc;
        DrawAtStart("Invoice Number: " + order.Invoice, Offset);

        if (!String.Equals(order.Customer.Address, "N/A"))
        {
            Offset = Offset + mediuminc;
            DrawAtStart("Address: " + order.Customer.Address, Offset);
        }

        if (!String.Equals(order.Customer.Phone, "N/A"))
        {
            Offset = Offset + mediuminc;
            DrawAtStart("Phone # : " + order.Customer.Phone, Offset);
        }

        Offset = Offset + mediuminc;
        DrawAtStart("Date: " + order.Date, Offset);

        Offset = Offset + smallinc;
        underLine = "-------------------------";
        DrawLine(underLine, largefont, Offset, 30);

        Offset = Offset + largeinc;

        InsertHeaderStyleItem("Name. ", "Price. ", Offset);

        Offset = Offset + largeinc;
        foreach (var itran in order.ItemTransactions)
        {
            InsertItem(itran.Item.Name + " x " + itran.Quantity, itran.Total.CValue, Offset);
            Offset = Offset + smallinc;
        }
        foreach (var dtran in order.DealTransactions)
        {
            InsertItem(dtran.Deal.Name, dtran.Total.CValue, Offset);
            Offset = Offset + smallinc;

            foreach (var di in dtran.Deal.DealItems)
            {
                InsertItem(di.Item.Name + " x " + (dtran.Quantity * di.Quantity), "", Offset);
                Offset = Offset + smallinc;
            }
        }

        underLine = "-------------------------";
        DrawLine(underLine, largefont, Offset, 30);

        Offset = Offset + largeinc;
        InsertItem(" Net. Total: ", order.Total.CValue, Offset);

        if (!order.Cash.Discount.IsZero())
        {
            Offset = Offset + smallinc;
            InsertItem(" Discount: ", order.Cash.Discount.CValue, Offset);
        }

        Offset = Offset + smallinc;
        InsertHeaderStyleItem(" Amount Payable: ", order.GrossTotal.CValue, Offset);

        Offset = Offset + largeinc;
        String address = shop.Address;
        DrawSimpleString("Address: " + address, minifont, Offset, 15);

        Offset = Offset + smallinc;
        String number = "Tel: " + shop.Phone1 + " - OR - " + shop.Phone2;
        DrawSimpleString(number, minifont, Offset, 35);

        Offset = Offset + 7;
        underLine = "-------------------------------------";
        DrawLine(underLine, largefont, Offset, 0);

        Offset = Offset + mediuminc;
        String greetings = "Thanks for visiting us.";
        DrawSimpleString(greetings, mediumfont, Offset, 28);

        Offset = Offset + mediuminc;
        underLine = "-------------------------------------";
        DrawLine(underLine, largefont, Offset, 0);

        Offset += (2 * mediuminc);
        string tip = "TIP: -----------------------------";
        InsertItem(tip, "", Offset);

        Offset = Offset + largeinc;
        string DrawnBy = "Meganos Softwares: 0312-0459491 - OR - 0321-6228321";
        DrawSimpleString(DrawnBy, minifont, Offset, 15);
    }
}

enter image description here

由于我们的需求,这里已经注释了一些添加图像的代码,您可以在页眉处添加您的标志,如第二张图片所示。

enter image description here


一些项目有多行,例如“管理员提供了折扣的大比萨饼£2”,如何调整其他项目或下面的详细信息的偏移量。 - naeemshah1

1
我假设您打印的输出不是在热敏打印机上,而是在一台普通的高分辨率位图能力打印机上?如果是这样,您的问题基本上需要您生成一个位图/ PDF或其他图形描述,然后将其发送到打印机。由于您提供的示例图像仅包含文本,则您的任务仅是使用坐标和字体大小布置此文本。然而,您将面临的问题是获取正确的字体并完全正确地获取间距和字距等。这将是我的起点。看看能否找到与图像中相似的字体。在Photoshop/GIMP或类似软件中进行测试。使您的热敏打印的照片成为背景层,然后看看能否将前景设置为相同。一旦您可以复制它,就去完成软件。从您的Photoshop模型中选择文本坐标。

字体对我来说并不是很重要,我的目标是找到编写热敏打印机漂亮收据的最佳/最简单方法,我遇到的问题在于格式化/排列文本以适应收据,例如设置纸张大小,在纸张左侧或右侧排列文本等等... - Nic
在你的例子中使用DrawString函数有什么问题吗?你是否在寻找放置文本的正确坐标时遇到了问题?如果是,我建议尝试一下找到纸张宽度。 - morishuz
我认为这不是最好的方式,但如果你能证明,我将非常高兴。 - Nic

1
你可以将收据转换为位图图像,并使用XpsDocuentGenerator类。或者使用开源库,如MigraDocs,在其中将其转换为PDF并发送到打印机。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接