POS系统中DataGridView数据打印换行问题求助
解决POS系统收据打印的三个核心问题
我来帮你一步步搞定POS系统里收据打印的问题,咱们从你遇到的几个痛点逐个梳理:
1. 最初DataGridView数据拼接的语法错误
你最初写的string list那行明显语法不对,C#里不能直接用逗号把多个单元格值拼到一个字符串里,而且还没加循环,根本没法遍历所有行的数据。正确的思路应该是在打印循环里逐行处理每个商品的信息,而不是提前生硬拼接成一个字符串(如果需要提前拼接,也得用循环累加)。
2. 数据库插入循环的优化问题
你现在的插入代码每次循环都新建OleDbConnection和OleDbCommand,这会造成不必要的资源开销,而且直接拼接SQL语句有SQL注入风险,必须改成参数化查询。优化后的代码应该是这样:
// 把连接和命令放在循环外面,避免重复创建销毁资源 string connStr = @"Provider=Microsoft.ACE.OLEDB.12.0;Data Source=TS-POS.accdb"; using (OleDbConnection conn = new OleDbConnection(connStr)) { conn.Open(); // 提前定义参数化的SQL语句 string sql = "INSERT INTO Sales (InvoiceNo, InvoiceDate, Pname, Pprice, Bcode, Staff) VALUES (@InvoiceNo, @InvoiceDate, @Pname, @Pprice, @Bcode, @Staff)"; using (OleDbCommand cmd = new OleDbCommand(sql, conn)) { // 添加参数(注意OleDb参数是按顺序匹配的,不是严格按名称) cmd.Parameters.Add("@InvoiceNo", OleDbType.VarChar); cmd.Parameters.Add("@InvoiceDate", OleDbType.Date); cmd.Parameters.Add("@Pname", OleDbType.VarChar); cmd.Parameters.Add("@Pprice", OleDbType.Currency); // 假设价格是货币类型,可根据实际调整 cmd.Parameters.Add("@Bcode", OleDbType.VarChar); cmd.Parameters.Add("@Staff", OleDbType.VarChar); foreach (DataGridViewRow row in dataGridView1.Rows) { // 跳过DataGridView的新增空行 if (row.IsNewRow) continue; // 给参数赋值 cmd.Parameters["@InvoiceNo"].Value = txtInvoiceNo.Text; cmd.Parameters["@InvoiceDate"].Value = dateTimePicker1.Value; cmd.Parameters["@Pname"].Value = row.Cells[1].Value; cmd.Parameters["@Pprice"].Value = row.Cells[3].Value; cmd.Parameters["@Bcode"].Value = row.Cells[0].Value; cmd.Parameters["@Staff"].Value = lblUser.Text; cmd.ExecuteNonQuery(); } } }
这样不仅性能更好,还彻底避免了SQL注入的风险,代码也更整洁易维护。
3. 打印内容全部挤在同一行的问题
这个问题的核心是你在循环里一直用同一个pointList的Y坐标,所有商品信息都画在了同一个位置,自然会重叠在一行。解决方法很简单:每次循环时把Y坐标往下移动一段距离(比如根据字体高度来计算),同时要确保Bitmap的高度足够容纳所有内容,不然会被截断。
修正后的printready方法代码:
private void printready() { string welcome = "Thank You For Visiting Dulabk"; string InvoiceNo = txtInvoiceNo.Text; string InvoiceDate = dateTimePicker1.Value.ToLongDateString(); // 动态计算Bitmap总高度:标题行预留高度 + 商品行数*行高 + 分隔线高度 int rowHeight = 25; // 每行商品的高度,可根据字体大小调整 int totalHeight = 90 + dataGridView1.Rows.Count * rowHeight + 20; Bitmap bitm = new Bitmap(welcome.Length * 30, totalHeight); using (Graphics graphic = Graphics.FromImage(bitm)) { Font newfont = new Font("Arial Black", 8); Font newfont2 = new Font("Arial Black", 10); Font itemFont = new Font("Arial Bold", 11); PointF point = new PointF(40f, 2f); SolidBrush black = new SolidBrush(Color.Black); SolidBrush white = new SolidBrush(Color.White); // 填充白色背景 graphic.FillRectangle(white, 0, 0, bitm.Width, bitm.Height); // 打印发票号 graphic.DrawString(InvoiceNo, newfont2, black, point); // 打印日期,Y坐标往下移 PointF pointPrice = new PointF(15f, point.Y + 30f); graphic.DrawString(InvoiceDate, newfont2, black, pointPrice); // 打印欢迎语,继续下移 PointF pointBar = new PointF(10f, pointPrice.Y + 20f); graphic.DrawString(welcome, newfont2, black, pointBar); // 打印商品列表,初始Y坐标从欢迎语下方开始 PointF pointList = new PointF(10f, pointBar.Y + 20f); foreach (DataGridViewRow row in dataGridView1.Rows) { if (row.IsNewRow) continue; // 拼接商品信息(可以调整格式,比如加上空格分隔更美观) string itemText = $"{row.Cells[0].Value} {row.Cells[1].Value} {row.Cells[2].Value}"; graphic.DrawString(itemText, itemFont, black, pointList); // 关键:每次循环后Y坐标增加rowHeight,让下一行内容错开 pointList = new PointF(pointList.X, pointList.Y + rowHeight); } // 打印分隔线,在最后一行商品下方 PointF pointItemEnd = new PointF(10f, pointList.Y + 10f); graphic.DrawString("----------", newfont2, black, pointItemEnd); } using (MemoryStream Mmst = new MemoryStream()) { bitm.Save(Mmst, ImageFormat.Jpeg); pictureBox1.Image = bitm; pictureBox1.Width = bitm.Width; pictureBox1.Height = bitm.Height; } }
这里的关键修改点:
- 动态计算Bitmap的总高度,避免内容被截断
- 每次打印商品行后,把
pointList的Y坐标增加rowHeight,让每行内容错开 - 用foreach循环遍历行,更简洁,同时跳过新增行(防止空数据打印)
这样调整后,你的收据应该就能正常分行打印所有商品信息了。
内容的提问来源于stack exchange,提问作者Joey Arnanzo
相关产品推荐
相关产品推荐

