最近给客户做一个MES项目中,其中有一些表单打印需求,因为不能使用第三方的打印组件,刚开始想用excel套打,发现问题也不少,客户机可能不能安装excel,最后想来想去还不如自己draw一个表单打印得了。本文将详细介绍如何使用GDT+做一个表单打印,因为时间比较紧张,写的还不完善,够项目用了,也就一直没去完善了,估计不少项目上的兄弟都一样。。。
将打印纸张切分为多行,多列,再根据行列索引定位。
比较麻烦的就是合并行或列,这里并没有真正的合并,而是通过FillRectangle来处理,算是讨巧了。
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("裁剪跟踪文件-" + "" + "打印完成...");
}
}
}
本文作者:技术老小子
本文链接:
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!