如何将数据库中的列字段添加到字符串生成器 c# 并实现两列布局

Posted

技术标签:

【中文标题】如何将数据库中的列字段添加到字符串生成器 c# 并实现两列布局【英文标题】:How To Add Column field from database to String Builder c# and implementing Two Column layout 【发布时间】:2021-11-06 01:49:37 【问题描述】:

我正在尝试在收据的字符串生成器上进行布局设计,但它没有这样做我也尝试将从数据库表中获取的列字段附加到字符串生成器上,但它也没有打印它。任何人都可以请展示正确的方法吗?是怎么做到的?

StringBuilder sb = new StringBuilder();
    
sb.Append("<b>--- DINE IN --- </b>\n\n");
        
// Revert to align left and normal weight
sb.Append(ESC + "a" + (char)0);
sb.Append(ESC + "E" + (char)0);
        
//this are from the database table columns
printJobQuery.CheckNumber.ToString();
printJobQuery.CheckStartDate.ToString();
printJobQuery.CheckStartDate.ToString(); // convert to time
        
foreach (var OrderLst in printJobLineQuery)

    OrderLst.Quantity.ToString();
    OrderLst.ItemName.ToString();
    OrderLst.ActualPrice.ToString();

        
printJobQuery.TableDescription.ToString();
printJobQuery.CustomerNumber.ToString();
printJobQuery.EmpId.ToString();
// end here database column
        
sb.Append("Check: @printJobQuery.CheckStartDate.ToString()");
        
// Format some text
sb.Append("This is it's header text of order receipt\n");
sb.AppendFormat("This is the Body Content of Header Receipt\n\n", host);
        
sb.Append("Can Add More Content Here,\n\n");
        
//Footer
sb.Append("Footer Part of the Receipt can " +
          "\nAdd Time and Date of Order \n\n");
        
// Feed and cut paper
sb.Append(GS + "V\x41\0");
        
return Encoding.Default.GetBytes(sb.ToString());

布局应该是这样的

1 牛奶 :: 25.00 1 份三明治 :: 30.00

【问题讨论】:

一个突出的事情是你调用ToString 方法的很多事情没有对结果做任何事情,例如printJobQuery.CheckNumber.ToString(); - 这应该说sb.AppendLine(printJobQuery.CheckNumber.ToString()); 吗? 如何将字体文本大小和字体粗细增加到粗体? 您的问题中的文字大小和字体粗细?还是在您要创建的收据中? 让我只想增加字符串生成器的页眉部分和正文和页脚部分的正常大小,我可以这样做吗?即页眉区域中的每个字符串都将大于正文和页脚区域。 你不能单独使用 StringBuilder 来做到这一点。是否以及如何完成将取决于您使用的打印机 - 看起来它接受一些用于不同操作(例如进纸、剪切、对齐)的控制代码,并且代码中的一行似乎正在使用 html 标记.打印机的文档是最好的查看位置。 【参考方案1】:

在这个答案中,我假设您要打印收据的纸张是固定宽度,因此限制为每行的最大字符数,以防止您的 2 列项目信息换行以丑陋的方式。我还假设您的打印机使用的是等宽字体,否则所有关于一行可以容纳多少个字符的计算都会超出窗口。

这个类应该可以帮助您开始格式化列表中的一些数据,以便输出在 2 列中显示列表的内容。

namespace ***69117445TwoColumns

    using System.Text;

    public class Receipt
    
        private readonly PrintJobQuery query;
        private readonly int columnWidth;

        public Receipt(PrintJobQuery query, int columnWidth)
        
            this.query = query;
            this.columnWidth = columnWidth;
        

        public string FormatForPrinting()
        
            var sb = new StringBuilder();
            sb.AppendLine(query.CheckNumber.ToString());
            sb.AppendLine(query.CheckStartDate.ToString());

            for (var i = 0; i < query.OrderLst.Count; i += 2)
            
                var item1 = this.FormatItem(query.OrderLst[i]);
                if (i + 1 < query.OrderLst.Count)
                
                    // then we've got 2 items to display on this line
                    var item2 = this.FormatItem(query.OrderLst[i + 1]);
                    sb.AppendLine($"item1   item2");
                
                else
                
                    // then we've got only 1 item to display so pad the
                    // remainder of the line with spaces
                    sb.Append(item1);
                    sb.AppendLine(new string(' ', this.columnWidth + 3));
                
            

            sb.AppendLine(query.TableDescription);
            sb.AppendLine(query.CustomerNumber.ToString());
            sb.AppendLine(query.EmpId.ToString());

            return sb.ToString();
        

        private string FormatItem(OrderItem item)
        
            var quantity = item.Quantity.ToString();
            var name = item.ItemName;
            var price = item.ActualPrice.ToString("#0.00");

            // If the format is <quantity> <item name> :: <price>
            // then the total string length without any truncation will be
            // the total length of the 3 data items plus 5 (3 spaces and 2
            // colons)
            var stringLengthWithoutTruncation = quantity.Length + name.Length + price.Length + 5;

            // Either truncate parts of the line or pad with spaces to make it the right length
            // for the column width.
            var overflow = stringLengthWithoutTruncation - this.columnWidth;
            var desiredNameLength = name.Length - overflow;
            if (overflow > 0)
            
                // The line is wider than the column, so we need to truncate the name
                name = name.Substring(0, desiredNameLength) + " ::";
            

            if (overflow < 0)
            
                // The string is narrower than the column, so pad the item name with spaces
                name = name.PadRight(desiredNameLength) + " ::";
            

            return $"quantity name price";
        
    

因此,您创建Receipt 的实例,提供PrintJobQuery 实例,其中包含您要格式化为收据的数据以及两列中每一列的字符长度。然后,FormatForPrinting 方法将返回一个字符串,其中包含该数据的格式化版本,您可以将其发送到打印机。

如果任何商品的数量、名称和价格在格式化时太长而无法适应所需的列宽,则会截断名称以使其适合。如果任何商品的数量、名称和价格在格式化后短于所需的列宽,则名称右侧会用空格填充。

请不要问我如何将该字符串发送到打印机,我没有以编程方式与打印机等外围设备交互的经验,所以我只专注于如何实现 2列布局。

关于此实现与您的初始代码有何不同的几点需要注意...

需要打印的所有内容都传递给StringBuilderAppend... 方法之一 - 任何未传递给StringBuilder 的内容都不会包含在输出中 StringBuilder 有两种常用的方法——AppendAppendLineAppend 只是将字符添加到字符串的末尾,而AppendLine 在字符后面附加任何字符序列,表示用户操作系统中的换行符。鉴于您将输出发送到打印机,AppendLine 的使用是否合适将在很大程度上取决于打印机的品牌和型号以及它认为是什么换行符,我有没有这方面的经验。

我不得不对您用于保存数据的模型类做出一些假设,这是我使用过的类,但它应该足够简单,以适应 Receipt 类以匹配您的实际模型类。

首先,PrintJobQuery,它保存了收据上的所有数据:

namespace ***69117445TwoColumns

    using System;
    using System.Collections.Generic;

    public class PrintJobQuery
    
        public int CheckNumber  get; set; 
        public DateTime CheckStartDate  get; set; 
        public string TableDescription  get; set; 
        public int CustomerNumber  get; set; 
        public int EmpId  get; set; 
        public List<OrderItem> OrderLst  get;  = new List<OrderItem>();
    

还有OrderItem,它保存了要在收据上详细显示的2列项目中显示的单个项目的详细信息:

namespace ***69117445TwoColumns

    public class OrderItem
    
        public int Quantity  get; set; 
        public string ItemName  get; set; 
        public decimal ActualPrice  get; set; 
    

我也对此进行了测试 - 这是我的单元测试:

namespace ***69117445TwoColumns

    using System;
    using System.Collections.Generic;
    using System.Diagnostics;
    using Xunit;

    public class UnitTest1
    
        [Theory]
        [InlineData(50, 103)]
        [InlineData(40, 83)]
        [InlineData(30, 63)]
        //[InlineData(5, 63)]
        public void FourItemsTest(int columnWidth, int receiptWidth)
        
            // Arrange
            var query = new PrintJobQuery
            
                CheckNumber = 123456,
                CheckStartDate = DateTime.Now,
                CustomerNumber = 123,
                EmpId = 3,
                TableDescription = "Rectangular with a nice tablecloth",
            ;
            query.OrderLst.AddRange(TestData.FourItems);
            var receipt = new Receipt(query, columnWidth);

            // Act
            var output = receipt.FormatForPrinting();

            // Assert
            Debug.WriteLine($"FourItemsTest with columnWidth columnWidth and expected receiptWidth receiptWidth");
            Debug.WriteLine(output);
            var lines = output.Split(Environment.NewLine);
            Assert.Equal(receiptWidth, lines[2].Length);
            Assert.Equal(receiptWidth, lines[3].Length);
        

        [Theory]
        [InlineData(50, 103)]
        [InlineData(40, 83)]
        [InlineData(30, 63)]
        public void FiveItemsTest(int columnWidth, int receiptWidth)
        
            // Arrange
            var query = new PrintJobQuery
            
                CheckNumber = 123456,
                CheckStartDate = DateTime.Now,
                CustomerNumber = 123,
                EmpId = 3,
                TableDescription = "Rectangular with a nice tablecloth",
            ;
            query.OrderLst.AddRange(TestData.FiveItems);
            var receipt = new Receipt(query, columnWidth);

            // Act
            var output = receipt.FormatForPrinting();

            // Assert
            Debug.WriteLine($"FiveItemsTest with columnWidth columnWidth and expected receiptWidth receiptWidth");
            Debug.WriteLine(output);
            var lines = output.Split(Environment.NewLine);
            Assert.Equal(receiptWidth, lines[2].Length);
            Assert.Equal(receiptWidth, lines[3].Length);
            Assert.Equal(receiptWidth, lines[4].Length);
        
    

如果您不熟悉 xunit 或其[Theory] 属性,请阅读here。

这是提供测试数据的类:

namespace ***69117445TwoColumns

    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Text;

    public class TestData
    
        public static List<OrderItem> FourItems
        
            get
            
                var list = new List<OrderItem>();
                list.Add(new OrderItem  Quantity = 1, ItemName = "Penny chew", ActualPrice = 0.01M );
                list.Add(new OrderItem  Quantity = 1, ItemName = "Something expensive", ActualPrice = 350000.00M );
                list.Add(new OrderItem  Quantity = 2, ItemName = "Something with a really really really long name", ActualPrice = 25.00M );
                list.Add(new OrderItem  Quantity = 300, ItemName = "Lots of something", ActualPrice = 500.00M );
                return list;
            
        

        public static List<OrderItem> FiveItems
        
            get
            
                var list = new List<OrderItem>();
                list.Add(new OrderItem  Quantity = 1, ItemName = "Penny chew", ActualPrice = 0.01M );
                list.Add(new OrderItem  Quantity = 1, ItemName = "Something expensive", ActualPrice = 350000.00M );
                list.Add(new OrderItem  Quantity = 2, ItemName = "Something with a really really really long name", ActualPrice = 25.00M );
                list.Add(new OrderItem  Quantity = 300, ItemName = "Lots of something", ActualPrice = 500.00M );
                list.Add(new OrderItem  Quantity = 2, ItemName = "Wine", ActualPrice = 5.00M );
                return list;
            
        
    

这是测试写入调试控制台的内容,以便您可以看到格式化输出的样子:

FourItemsTest with columnWidth 50 and expected receiptWidth 103
123456
15/09/2021 20:18:29
1 Penny chew                               :: 0.01   1 Something expensive                 :: 350000.00
2 Something with a really really really l :: 25.00   300 Lots of something                    :: 500.00
Rectangular with a nice tablecloth
123
3

FourItemsTest with columnWidth 30 and expected receiptWidth 63
123456
15/09/2021 20:18:29
1 Penny chew           :: 0.01   1 Something expen :: 350000.00
2 Something with a re :: 25.00   300 Lots of somethin :: 500.00
Rectangular with a nice tablecloth
123
3

FourItemsTest with columnWidth 40 and expected receiptWidth 83
123456
15/09/2021 20:18:29
1 Penny chew                     :: 0.01   1 Something expensive       :: 350000.00
2 Something with a really reall :: 25.00   300 Lots of something          :: 500.00
Rectangular with a nice tablecloth
123
3

FiveItemsTest with columnWidth 50 and expected receiptWidth 103
123456
15/09/2021 20:18:29
1 Penny chew                               :: 0.01   1 Something expensive                 :: 350000.00
2 Something with a really really really l :: 25.00   300 Lots of something                    :: 500.00
2 Wine                                     :: 5.00                                                     
Rectangular with a nice tablecloth
123
3

FiveItemsTest with columnWidth 40 and expected receiptWidth 83
123456
15/09/2021 20:18:29
1 Penny chew                     :: 0.01   1 Something expensive       :: 350000.00
2 Something with a really reall :: 25.00   300 Lots of something          :: 500.00
2 Wine                           :: 5.00                                           
Rectangular with a nice tablecloth
123
3

FiveItemsTest with columnWidth 30 and expected receiptWidth 63
123456
15/09/2021 20:18:29
1 Penny chew           :: 0.01   1 Something expen :: 350000.00
2 Something with a re :: 25.00   300 Lots of somethin :: 500.00
2 Wine                 :: 5.00                                 
Rectangular with a nice tablecloth
123
3

请注意,此代码并非生产就绪,它只是一个示例,您可以将其用作实现自己代码的指导。

特别是,有一个我没有处理的错误情况 - 如果一个项目的数量和价格的字符串表示,加上空格字符,比所需的列宽长怎么办?在这种情况下,我的代码会尝试将项目的名称截断为长度为负的字符串,这会导致未处理的异常

  Message: 
System.ArgumentOutOfRangeException : Length cannot be less than zero. (Parameter 'length')

  Stack Trace: 
String.Substring(Int32 startIndex, Int32 length)
Receipt.FormatItem(OrderItem item) line 66
Receipt.FormatForPrinting() line 24
UnitTest1.FourItemsTest(Int32 columnWidth, Int32 receiptWidth) line 30

要查看实际情况,请取消注释 FourItemsTest 方法上的 [InlineData(5, 63)] 属性。

在您的情况下,我不会试图猜测如何最好地处理这种情况。您只需要确保所需的列宽足以容纳它需要包含的信息。

希望这是有用的:-)

【讨论】:

以上是关于如何将数据库中的列字段添加到字符串生成器 c# 并实现两列布局的主要内容,如果未能解决你的问题,请参考以下文章

改变模式生成器中的列长度?

C#如何读取数据库表中每一列的数据分别赋值给数组

C#读取EXCEL中的信息,并保存到数据库

MS Access中的列过多时如何转置数据

使用 SqlBulkCopy 将 DataTable 中的列映射到 SQL 表

如何根据 c# winforms 中的列日期和名称将 sql 中的数据输入到特定的 datagridview 单元格中