编辑
2025-09-19
C#
00

目录

基本思路
直接上代码
Table 绘制类
调用打印类
注意事项

最近给客户做一个MES项目中,其中有一些表单打印需求,因为不能使用第三方的打印组件,刚开始想用excel套打,发现问题也不少,客户机可能不能安装excel,最后想来想去还不如自己draw一个表单打印得了。本文将详细介绍如何使用GDT+做一个表单打印,因为时间比较紧张,写的还不完善,够项目用了,也就一直没去完善了,估计不少项目上的兄弟都一样。。。

基本思路

将打印纸张切分为多行,多列,再根据行列索引定位。

比较麻烦的就是合并行或列,这里并没有真正的合并,而是通过FillRectangle来处理,算是讨巧了。

直接上代码

Table 绘制类

C#
using QRCoder; using System; using System.Collections.Generic; using System.Drawing; using System.Linq; using System.Reflection.Emit; using System.Text; using System.Threading.Tasks; using static System.Windows.Forms.AxHost; namespace Printer { public class Table { /// <summary> /// 表格多少列 /// </summary> public List<Column> Columns { get; set; } = new List<Column>(); /// <summary> /// 列表多少行 /// </summary> public List<Row> Rows { get; set; } = new List<Row>(); public List<Position> Positions { get; set; } = new List<Position>(); public Position? GetPosition(int rowIndex, int columnIndex) { return Positions.Find(x => x.RowIndex == rowIndex && x.ColumnIndex == columnIndex); } /// <summary> /// 取得列宽 /// </summary> /// <param name="columnIndex"></param> /// <returns></returns> public int GetColumnWidth(int columnIndex) { return Columns[columnIndex].Width; } /// <summary> /// 取得行高 /// </summary> /// <param name="rowIndex"></param> /// <returns></returns> public int GetRowHeight(int rowIndex) { return Rows[rowIndex].Height; } /// <summary> /// 第几行,第几列开始,合并几列 /// </summary> [Obsolete("可以用MergeCelll替换了")] public Dictionary<(int, int), int> ColumnMerge { get; set; } = new Dictionary<(int, int), int>(); [Obsolete("可以用MergeCelll替换了")] public Dictionary<(int, int), int> RowMerge { get; set; } = new Dictionary<(int, int), int>(); /// <summary> /// key为从几行,几列,value为到几行,几列 /// </summary> public Dictionary<(int, int), (int, int)> MergeCelll { get; set; } = new Dictionary<(int, int), (int, int)>(); /// <summary> /// 画出基本表格 /// </summary> /// <param name="graphics"></param> /// <param name="startX"></param> /// <param name="startY"></param> public void Draw(Graphics graphics, int startX, int startY) { Pen pen = new Pen(Color.Black, 1); // 设置表格线条颜色和宽度 // 绘制表格 int currentY = startY; for (int i = 0; i < this.Rows.Count; i++) { int currentX = startX; for (int j = 0; j < this.Columns.Count; j++) { this.Positions.Add(new Position(i, j, new Point(currentX, currentY))); // 绘制单元格 graphics.DrawRectangle(pen, currentX, currentY, this.Columns[j].Width, this.Rows[i].Height); currentX += this.Columns[j].Width; } currentY += this.Rows[i].Height; } //列合并 foreach (var item in this.ColumnMerge) { var mWidth = 0; for (int i = 0; i < item.Value; i++) { mWidth += this.GetColumnWidth(item.Key.Item2 + i); } graphics.FillRectangle(new SolidBrush(System.Drawing.Color.White) , this.GetPosition(item.Key.Item1, item.Key.Item2).Point.X + 1 , this.GetPosition(item.Key.Item1, item.Key.Item2).Point.Y + 1 , mWidth - 2 , this.GetRowHeight(item.Key.Item1)); graphics.DrawRectangle(pen , this.GetPosition(item.Key.Item1, item.Key.Item2).Point.X , this.GetPosition(item.Key.Item1, item.Key.Item2).Point.Y , mWidth , this.GetRowHeight(item.Key.Item1)); } //行合并 foreach (var item in this.RowMerge) { var mHeight = 0; for (int i = 0; i < item.Value; i++) { mHeight += this.GetRowHeight(item.Key.Item1 + i); } graphics.FillRectangle(new SolidBrush(System.Drawing.Color.White) , this.GetPosition(item.Key.Item1, item.Key.Item2).Point.X + 1 , this.GetPosition(item.Key.Item1, item.Key.Item2).Point.Y + 1 , this.GetColumnWidth(item.Key.Item2) - 2 , mHeight - 2); } //通过cell区域合并 foreach (var item in this.MergeCelll) { int startRowIndex = item.Key.Item1; int startColIndex = item.Key.Item2; int w = 0; int h = 0; for (int i = startRowIndex; i <= item.Value.Item1; i++) { h += this.GetRowHeight(i); } for (int i = startColIndex; i <= item.Value.Item2; i++) { w += this.GetColumnWidth(i); } graphics.FillRectangle(new SolidBrush(System.Drawing.Color.White) , this.GetPosition(startRowIndex, startColIndex).Point.X , this.GetPosition(startRowIndex, startColIndex).Point.Y , w , h); graphics.DrawRectangle(pen , this.GetPosition(startRowIndex, startColIndex).Point.X , this.GetPosition(startRowIndex, startColIndex).Point.Y , w , h); } } /// <summary> /// 在指定的单元格位置绘制图片。 /// </summary> /// <param name="graphics">用于绘制的Graphics对象。</param> /// <param name="position">指定单元格的位置信息。</param> /// <param name="image">要绘制的图片。</param> public void DrawImageInCell(Graphics graphics, Position position, Image image, Point offset) { // 计算图片的绘制区域。这里我们直接使用图片的原始尺寸, // 但您也可以根据需要调整图片的大小以适应单元格的尺寸。 Rectangle imageRect = new Rectangle(position.Point.X + offset.X, position.Point.Y + offset.Y, image.Width, image.Height); // 绘制图片 graphics.DrawImage(image, imageRect); } public void DrawQRCode(Graphics graphics, string code, Position position, Point offset) { var data = QRCodeGenerator.GenerateQrCode(code, QRCodeGenerator.ECCLevel.Q , false, false, QRCodeGenerator.EciMode.Default); QRCode qcode = new QRCode(data); var img = qcode.GetGraphic(5, Color.Black, Color.White, false); this.DrawImageInCell(graphics, position, img, offset); } /// <summary> /// 在指定的单元格位置绘制文本。 /// </summary> /// <param name="graphics">用于绘制的Graphics对象。</param> /// <param name="position">指定单元格的位置信息。</param> /// <param name="text">要绘制的文本。</param> /// <param name="font">文本的字体。</param> /// <param name="brush">用于填充文本的Brush对象。</param> public void DrawTextInCell(Graphics graphics, Position position, string text, Font font, Brush brush , int mergeRow = 0, int mergeColumn = 0, StringFormat stringFormat = null) { SizeF sizeF = MeasureTextSize(graphics, text, font); RectangleF layoutRectangle; // 确定文本布局区域 if (mergeRow == 0) { if (mergeColumn == 0) { layoutRectangle = new RectangleF(position.Point.X, position.Point.Y, this.GetColumnWidth(position.ColumnIndex), this.GetRowHeight(position.RowIndex)); } else { int x = this.GetColumnWidth(position.ColumnIndex); for (int i = 1; i < mergeColumn; i++) { x += this.GetColumnWidth(position.ColumnIndex + i); } //设置填充区域 layoutRectangle = new RectangleF(position.Point.X, position.Point.Y, x, this.GetRowHeight(position.RowIndex)); } } else { int rowHeight = this.GetRowHeight(position.RowIndex); for (int i = 0; i < mergeRow; i++) { rowHeight += this.GetRowHeight(position.RowIndex + i); } layoutRectangle = new RectangleF(position.Point.X, position.Point.Y + (rowHeight - sizeF.Height) / 2, this.GetColumnWidth(position.ColumnIndex), this.GetRowHeight(position.RowIndex)); } //// 设置文本格式,例如,使文本居中显示 if (stringFormat == null) { stringFormat = new StringFormat { Alignment = StringAlignment.Center, LineAlignment = StringAlignment.Center, }; } // 绘制文本 graphics.DrawString(text, font, brush, layoutRectangle, stringFormat); } /// <summary> /// 计算指定文本的尺寸。 /// </summary> /// <param name="graphics">用于测量文本的Graphics对象。</param> /// <param name="text">要测量的文本。</param> /// <param name="font">文本的字体。</param> /// <returns>文本的尺寸(宽度和高度)。</returns> public static SizeF MeasureTextSize(Graphics graphics, string text, Font font) { // 测量文本的尺寸 SizeF textSize = graphics.MeasureString(text, font); return textSize; } } } public class Position { public int RowIndex { get; set; } = 0; public int ColumnIndex { get; set; } = 0; // 保存单元格左上角的位置 public Point Point { get; set; } // 可以添加一个构造函数来初始化Position对象 public Position(int rowIndex, int columnIndex, Point point) { RowIndex = rowIndex; ColumnIndex = columnIndex; Point = point; } } public class Row { public Row() { } public Row(int height) { this.Height = height; } public int Height { get; set; } = 0; } public class Column { public Column() { } public Column(int width) { this.Width = width; } public int Width { get; set; } = 0; }

调用打印类

C#
public class CutTruckPrinter { public Action<string> Callback; Table table = new Table(); private PrintDocument printDocument = new PrintDocument(); private string apiUrl = ""; public bool DoTask() { try { if (Callback != null) { Callback("裁剪跟踪文件-" + "" + "打印中..."); } printDocument.PrintPage += new PrintPageEventHandler(PrintDocument_PrintPage); PrintDialog printDialog = new PrintDialog(); printDialog.Document = printDocument; printDocument.Print(); return true; } catch { if (Callback != null) { Callback("裁剪跟踪文件-" + "" + "打印失败..."); } return false; } } private void PrintDocument_PrintPage(object sender, PrintPageEventArgs e) { Graphics graphics = e.Graphics; // 表格的起始位置 int startX = 10; int startY = 4; //设置行高 for (int i = 0; i < 6; i++) { table.Rows.Add(new Row(30)); // 设置行高 } //item项需提前添加行数 for (int i = 0; i < 3; i++) { table.Rows.Add(new Row(30)); // 设置行高 table.Rows.Add(new Row(30)); // 设置行高 table.Rows.Add(new Row(30)); // 设置行高 table.Rows.Add(new Row(30)); // 设置行高 } //设置列宽 for (int i = 0; i < 6; i++) { table.Columns.Add(new Column(80)); // 设置列宽 } table.Columns[1].Width = 140; table.Columns[4].Width = 60; int xxx = 0; for (int i = 0; i < table.Columns.Count; i++) { xxx += table.GetColumnWidth(i); } #region "合并行" table.MergeCelll.Add((0, 0), (0, 5)); //打 孔 区 域 table.MergeCelll.Add((1, 0), (1, 5));//小车标识检查清单 table.MergeCelll.Add((3, 4), (4, 5));//L table.MergeCelll.Add((3, 1), (3, 3));//订单 table.MergeCelll.Add((4, 1), (4, 3));//项目 table.MergeCelll.Add((5, 0), (5, 1)); table.MergeCelll.Add((5, 2), (5, 3)); table.MergeCelll.Add((5, 4), (5, 5)); #endregion #region "合并动态行" int startdIndex = 6; for (int i = 0; i < 3 * 4; i++) { table.MergeCelll.Add((6 + i, 0), (6 + i, 2)); table.MergeCelll.Add((6 + i, 2), (6 + i, 3)); } for (int i = 0; i < 3; i++) { table.MergeCelll.Add((6 + i * 4, 4), (9 + i * 4, 5)); } //(6,0),(9,5) #endregion table.Draw(graphics, startX, startY); table.DrawTextInCell(graphics, table.GetPosition(0, 0), "打 孔 区 域", new Font("Arial", 12), Brushes.Black, 0, 6); table.DrawTextInCell(graphics, table.GetPosition(1, 0), "小车标识检查清单", new Font("Arial", 12), Brushes.Black, 0, 6); table.DrawTextInCell(graphics, table.GetPosition(2, 0), "乙 班", new Font("Arial", 10), Brushes.Black, 0, 0); table.DrawTextInCell(graphics, table.GetPosition(2, 1), "2024-02-2519:08:44", new Font("Arial", 10), Brushes.Black, 0, 0); table.DrawTextInCell(graphics, table.GetPosition(2, 2), "开 单 人", new Font("Arial", 10), Brushes.Black, 0, 0); table.DrawTextInCell(graphics, table.GetPosition(2, 3), "韩 天 淦", new Font("Arial", 10), Brushes.Black, 0, 0); table.DrawTextInCell(graphics, table.GetPosition(2, 4), "NC", new Font("Arial", 10), Brushes.Black, 0, 0); table.DrawTextInCell(graphics, table.GetPosition(2, 5), " CO4", new Font("Arial", 10), Brushes.Black, 0, 0); table.DrawTextInCell(graphics, table.GetPosition(3, 0), " 订单号", new Font("Arial", 10), Brushes.Black, 0, 0); table.DrawTextInCell(graphics, table.GetPosition(3, 1), " J30X44HK-NP18FR-014", new Font("Arial", 10), Brushes.Black, 0, 3); table.DrawTextInCell(graphics, table.GetPosition(4, 0), "项目名称", new Font("Arial", 10), Brushes.Black, 0, 0); table.DrawTextInCell(graphics, table.GetPosition(4, 1), "J30X-NP44HK-NP18FR", new Font("Arial", 10), Brushes.Black, 0, 3); table.DrawTextInCell(graphics, table.GetPosition(5, 0), "裁剪文件名", new Font("Arial", 10), Brushes.Black, 0, 2); table.DrawTextInCell(graphics, table.GetPosition(5, 2), "材料零件号", new Font("Arial", 10), Brushes.Black, 0, 2); table.DrawTextInCell(graphics, table.GetPosition(5, 4), "套数", new Font("Arial", 10), Brushes.Black, 0, 2); int idx = 0; for (int i = 0; i < 3; i++) { table.DrawTextInCell(graphics, table.GetPosition(6 + idx, 0), "J30X18FR44GK-FK-120B-A", new Font("Arial", 10), Brushes.Black, 0, 2); table.DrawTextInCell(graphics, table.GetPosition(6 + idx, 2), "PK-120B", new Font("Arial", 10), Brushes.Black, 0, 2); table.DrawTextInCell(graphics, table.GetPosition(7 + idx, 0), "材料批号20240219", new Font("Arial", 10), Brushes.Black, 0, 2); table.DrawTextInCell(graphics, table.GetPosition(7 + idx, 2), "无纺布", new Font("Arial", 10), Brushes.Black, 0, 2); table.DrawTextInCell(graphics, table.GetPosition(8 + idx, 0), "裁片数量400", new Font("Arial", 10), Brushes.Black, 0, 2); table.DrawTextInCell(graphics, table.GetPosition(8 + idx, 2), "疵点裁片数0", new Font("Arial", 10), Brushes.Black, 0, 2); table.DrawTextInCell(graphics, table.GetPosition(9 + idx, 0), "240223FH1008-15-101", new Font("Arial", 10), Brushes.Black, 0, 2); table.DrawQRCode(graphics, "A000"+i.ToString(), table.GetPosition(6+idx, 4), new Point(20, 10)); idx = idx + 4; } if (Callback != null) { Callback("裁剪跟踪文件-" + "" + "打印完成..."); } } }

image.png

注意事项

  • 打印机的纸张打印调整。
  • 还有一些小瑕疵,像图片对齐,就没有认真写了,基本就用offsest偏移设置了。
  • DrawTextInCell 这个过程写的比较混乱,有时间再改,哈哈。。。

本文作者:技术老小子

本文链接:

版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!