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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.27 10:09:14