文章目录
- 前言
- 相关内容了解
- 打印预览功能
- 1 创建winform工程
- 2 创建要打印的测试数据
- 3 绘制打印页
- 绘制页脚
- 绘制内容
- PrintPage事件
- 完整的代码工程
- 小节
前言
有这么个需求:DataTable中有一些数据是需要给显示或直接可以连接打印机进行打印的, 查阅了一下资料,发现官方就有组件PrintPreviewDialog和PrintDocument能实现这个功能。
相关内容了解
1 PrintPreviewDialog
是一个打印预览的对话框,有打印,缩放、页面显示、选择页数、关闭等按钮。
长得就是这个样子。
2 PrintDocument
类:是一个可以发送到打印机上的对象
3 PrintDocument.PrintPage
事件 当需要为当前页打印的输出时发生
4 PaperSize
类 指定纸张的大小
5 PageSettings.PaperSize
属性 获取或设置该页的纸张大小
6 PrinterSettings
类 指定打印时如何打印文档的信息,包括打印文档的打印机
7 PaperKind
枚举 指定标准纸张大小
8 Margins
类 指定打印页的边距尺寸
左上角的坐标点是(MarginBounds.X, MarginBounds.Y)
e.MarginBounds.Right = e.MarginBounds.Left + e.MarginBounds.Width
e.MarginBounds.Bottom = e.MarginBounds.Top + e.MarginBounds.Height
具体可以查看MSDN的介绍:
1、PrintDocument 类 https://learn.microsoft.com/zh-cn/dotnet/api/system.drawing.printing.printdocument?view=dotnet-plat-ext-7.0
2、Margins 类 https://learn.microsoft.com/zh-cn/dotnet/api/system.drawing.printing.margins?view=dotnet-plat-ext-7.0
打印预览功能
1 创建winform工程
先创建一个winform工程,在窗体上放置了一个Button,名称是btnPreView
打印预览的入口按钮,
还有一个PrintDocument组件,名称是myDocument
用于呈现要打印的内容。
2 创建要打印的测试数据
使用一个全局DataTable 名为saveTable
存放测试数据。这里创建了100 行* 5列的数据,每列分别是 序号、名称、当前值、描述和默认值这五列。当然这只是我赋予的意义,为了好记而已。
/// <summary>
/// 构建临时数据表
/// </summary>
/// <returns></returns>
private DataTable CreateTable()
{
DataTable dt = new DataTable();
DataColumn col1 = new DataColumn("Num", typeof(string)); //序号
DataColumn col2 = new DataColumn("Name", typeof(string)); //名称
DataColumn col3 = new DataColumn("Val", typeof(string)); //当前值
DataColumn col4 = new DataColumn("Des", typeof(string)); //描述
DataColumn col5 = new DataColumn("Set", typeof(string)); //默认值
dt.Columns.Add(col1);
dt.Columns.Add(col2);
dt.Columns.Add(col3);
dt.Columns.Add(col4);
dt.Columns.Add(col5);
Random random = new Random();
List<string> nameList = new List<string>
{
"A", "BB", "CCC", "D",
"E", "F", "G","H","II",
"JJ", "LL", "M"
};
List<string> tempList = new List<string>
{
"dsd", "sdfdgvre", "Hello", "Gilrs",
"Today", "YYYY", "dfgre","GSD","fdgfer",
"Wesd", "DLG", "fsdahfi;o"
};
for (int i = 0; i < 10; i++)
{
for (int j = 0; j < 10; j++)
{
DataRow dr = dt.NewRow();
//序号
dr[0] = "KK" + i.ToString("d2") + "." + j.ToString("d2");
//名称
dr[1] = nameList[j];
//当前值
if (j % 3 == 0)
{
dr[2] = random.NextDouble().ToString("f3");
}
else
{
dr[2] = i * j - random.Next(0, 30);
}
//描述
dr[3] = tempList[j];
//出厂值
dr[4] = random.NextDouble().ToString("f2");
//添加新行
dt.Rows.Add(dr);
}
}
return dt;
}
3 绘制打印页
这里的绘制分为三个部分,表头、内容和页脚。绘制方法都是大同小异,只是为了模块化,划分了不同区域。使用Draw
绘制,重要的是找到要绘制点的X 、Y 坐标,通过MeasureString
计算字体的高度, 其余的功能都还比较简单。
先看一下要打印的模板,类似下图。
绘制标题(页眉) 序号 名称
/// <summary>
/// 绘制标题头
/// </summary>
/// <param name="e">绘图对象</param>
/// <param name="tempTop">Y轴坐标</param>
/// <param name="tempFormat">字符串格式</param>
private void DrawTitle(System.Drawing.Printing.PrintPageEventArgs e,
int tempTop, StringFormat tempFormat)
{
//绘制水平线
e.Graphics.DrawLine(Pens.Black,
new Point(e.MarginBounds.Left, tempTop),
new Point(e.MarginBounds.Right, tempTop));
Font printFont = new Font(fontName, fontNum);
//计算字体的高度 K00ARk, 根据使用到的字进行组合成串
int fontHeight = (int)(e.Graphics.MeasureString("K00ARk", printFont).Height);
//画刷
SolidBrush brush = new SolidBrush(Color.Black);
//填充背景色
e.Graphics.FillRectangle(Brushes.LightBlue,
new RectangleF(e.MarginBounds.Left, tempTop, e.MarginBounds.Width, fontHeight));
//列的序号
int col = 0;
//列 序号
string drawString = TitleStr[col];
//矩形框
RectangleF rectangle = new RectangleF((int)cellLeft[col], tempTop,
(int)cellWidth[col], fontHeight);
e.Graphics.DrawString(drawString, printFont,
brush, rectangle, tempFormat);
col++;
//列 名称
drawString = TitleStr[col];
//水平向右平移 并设置矩形框的宽度
rectangle.X = cellLeft[col];
rectangle.Width = cellWidth[col];
e.Graphics.DrawString(drawString, printFont,
brush, rectangle, tempFormat);
col++;
//列 设定值
drawString = TitleStr[col];
//水平向右平移 并设置矩形框的宽度
rectangle.X = cellLeft[col];
rectangle.Width = cellWidth[col];
e.Graphics.DrawString(drawString, printFont,
brush, rectangle, tempFormat);
col++;
//列 默认值
drawString = TitleStr[col];
//水平向右平移 并设置矩形框的宽度
rectangle.X = cellLeft[col];
rectangle.Width = cellWidth[col];
e.Graphics.DrawString(drawString, printFont,
brush, rectangle, tempFormat);
}
绘制页脚
在底部添加公司信息和页码作为页脚。
/// <summary>
/// 绘制页脚
/// </summary>
/// <param name="e">绘图对象</param>
/// <param name="pageNo">页面序号</param>
private void DrawFooter(System.Drawing.Printing.PrintPageEventArgs e, int pageNo)
{
Font font = new Font("微软雅黑", 10);
//绘制直线
e.Graphics.DrawLine(new Pen(Color.Black, 1),
new Point(e.MarginBounds.Left, e.MarginBounds.Bottom),
new Point(e.MarginBounds.Right, e.MarginBounds.Bottom));
//公司信息标注
string tempStr = "Test for Windows(C) by 唠嗑一夏 Electric Corporation";
e.Graphics.DrawString(tempStr,
font, Brushes.Black,
e.MarginBounds.Left,
e.MarginBounds.Bottom+2);
//页脚序号
tempStr = pageNo.ToString();
e.Graphics.DrawString(tempStr,
font, Brushes.Black,
e.MarginBounds.Right - e.Graphics.MeasureString(tempStr, font).Width,
e.MarginBounds.Bottom+2);
}
绘制内容
一行一行的绘制DataTable中的数据内容。
/// <summary>
/// 绘制内容
/// </summary>
/// <param name="e">绘图对象</param>
/// <param name="printIndex">数据行索引</param>
/// <param name="tempTop">Y轴坐标</param>
/// <param name="tempFormat">字符串格式</param>
private void DrawContent(System.Drawing.Printing.PrintPageEventArgs e,
int printIndex, int tempTop, StringFormat tempFormat)
{
Font printFont = new Font(fontName, fontNum);
//计算字体的高度 K00ARk, 根据使用到的字进行组合成串
int fontHeight = (int)(e.Graphics.MeasureString("K00ARk", printFont).Height);
//画刷
SolidBrush brush = new SolidBrush(Color.Black);
//列的序号
int col = 0;
//列 序号
string drawString = saveTable.Rows[printIndex][0].ToString() + saveTable.Rows[printIndex][1].ToString();
//矩形框
RectangleF rectangle = new RectangleF((int)cellLeft[col], tempTop,
(int)cellWidth[col], fontHeight);
e.Graphics.DrawString(drawString, printFont,
brush, rectangle, tempFormat);
col++;
//列 名称
drawString = saveTable.Rows[printIndex][3].ToString();
//水平向右平移 并设置矩形框的宽度
rectangle.X = cellLeft[col];
rectangle.Width = cellWidth[col];
e.Graphics.DrawString(drawString, printFont,
brush, rectangle, tempFormat);
col++;
//列 设定值
drawString = saveTable.Rows[printIndex][2].ToString();
//水平向右平移 并设置矩形框的宽度
rectangle.X = cellLeft[col];
rectangle.Width = cellWidth[col];
e.Graphics.DrawString(drawString, printFont,
brush, rectangle, tempFormat);
col++;
//列 默认值
drawString = saveTable.Rows[printIndex][4].ToString();
//水平向右平移 并设置矩形框的宽度
rectangle.X = cellLeft[col];
rectangle.Width = cellWidth[col];
e.Graphics.DrawString(drawString, printFont,
brush, rectangle, tempFormat);
}
PrintPage事件
计算每列占的比例,在事件中判断是否需要换页以及是否绘制结束。
/// <summary>
/// 当前页打印发生的事件
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void MyDocument_PrintPage(object sender, System.Drawing.Printing.PrintPageEventArgs e)
{
//页面大小
PaperSize pageSize = e.PageSettings.PaperSize;
//根据页面大小,按比例划分列宽
//序号
cellWidth[0] = (float)(pageSize.Width * 0.098);
//描述
cellWidth[1] = (float)(pageSize.Width * 0.36);
//当前值
cellWidth[2] = (float)(pageSize.Width * 0.2);
//出厂值
cellWidth[3] = (float)(pageSize.Width * 0.1176);
//计算列的起始位置
cellLeft[0] = e.PageSettings.Margins.Left;
cellLeft[1] = cellLeft[0] + cellWidth[0];
cellLeft[2] = cellLeft[1] + cellWidth[1];
cellLeft[3] = cellLeft[2] + cellWidth[2];
//高度
int tempTop = e.MarginBounds.Top;
//是否新的页面
bool tempIsNewPage = true;
//设置打印字符串的格式
StringFormat tempStrFormat = new StringFormat
{
Alignment = StringAlignment.Near,
LineAlignment = StringAlignment.Center,
Trimming = StringTrimming.EllipsisCharacter
};
//设置字体
Font printFont = new Font("宋体", 9);
//计算字体的高度
int fontHeight = (int)e.Graphics.MeasureString("KK00.10序号", printFont).Height;
//指定高质量、低速度呈现
e.Graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
try
{
string printStr;
while(printPoint < saveTable.Rows.Count)
{//逐行分页打印所有参数
if (tempTop + fontHeight > e.MarginBounds.Bottom)
{//判断当前打印是否超出页面范围,决定是否换页
//打印页脚
DrawFooter(e, PageNo);
PageNo++;
//继续分页打印
e.HasMorePages = true;
return;
}
//打印当前页的内容
//KK00.00
printStr = saveTable.Rows[printPoint][0].ToString().Substring(5);
if (printStr.Equals("00") || tempIsNewPage)
{//一组数据的开始
tempIsNewPage = false;
tempTop += 2;
//绘制标题
DrawTitle(e, tempTop, tempStrFormat);
tempTop += fontHeight;
}
//绘制内容
DrawContent(e, printPoint, tempTop, tempStrFormat);
//更新Y轴高度
tempTop += fontHeight;
//序号自增
printPoint++;
//判断是否已经到终点
if(printPoint >= saveTable.Rows.Count)
{
//绘制页脚
DrawFooter(e, PageNo);
//重置打印参数
PageNo = 1;
printPoint = 0;
return;
}
}
}
catch
{
}
}
完整的代码工程
下面是完整的代码工程,设计界面的代码就不列出来了。
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Drawing.Printing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace PrintDemo
{
public partial class Form1 : Form
{
//打印序号
int printPoint = 0;
//页码
int PageNo = 1;
//表头名称
string[] TitleStr = new string[]
{
"序号", "名称", "当前值", "出厂值"
};
//列宽
float[] cellWidth = new float[4];
//列左侧的起始位置
float[] cellLeft = new float[4];
//数据表
DataTable saveTable;
string fontName = "宋体";
int fontNum = 9;
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
//数据
saveTable = CreateTable();
//打印预览按钮
btnPreView.Click += BtnPreView_Click;
//当前页打印发生的事件
myDocument.PrintPage += MyDocument_PrintPage;
}
/// <summary>
/// 构建临时数据表
/// </summary>
/// <returns></returns>
private DataTable CreateTable()
{
DataTable dt = new DataTable();
DataColumn col1 = new DataColumn("Num", typeof(string)); //序号
DataColumn col2 = new DataColumn("Name", typeof(string)); //名称
DataColumn col3 = new DataColumn("Val", typeof(string)); //当前值
DataColumn col4 = new DataColumn("Des", typeof(string)); //描述
DataColumn col5 = new DataColumn("Set", typeof(string)); //默认值
dt.Columns.Add(col1);
dt.Columns.Add(col2);
dt.Columns.Add(col3);
dt.Columns.Add(col4);
dt.Columns.Add(col5);
Random random = new Random();
List<string> nameList = new List<string>
{
"A", "BB", "CCC", "D",
"E", "F", "G","H","II",
"JJ", "LL", "M"
};
List<string> tempList = new List<string>
{
"dsd", "sdfdgvre", "Hello", "Gilrs",
"Today", "YYYY", "dfgre","GSD","fdgfer",
"Wesd", "DLG", "fsdahfi;o"
};
for (int i = 0; i < 10; i++)
{
for (int j = 0; j < 10; j++)
{
DataRow dr = dt.NewRow();
//序号
dr[0] = "KK" + i.ToString("d2") + "." + j.ToString("d2");
//名称
dr[1] = nameList[j];
//当前值
if (j % 3 == 0)
{
dr[2] = random.NextDouble().ToString("f3");
}
else
{
dr[2] = i * j - random.Next(0, 30);
}
//描述
dr[3] = tempList[j];
//出厂值
dr[4] = random.NextDouble().ToString("f2");
//添加新行
dt.Rows.Add(dr);
}
}
return dt;
}
/// <summary>
/// 打印预览
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void BtnPreView_Click(object sender, EventArgs e)
{
//打印预览对话框
PrintPreviewDialog dialog = new PrintPreviewDialog();
dialog.Document = myDocument;
dialog.ShowIcon = false;
dialog.ShowDialog();
}
/// <summary>
/// 当前页打印发生的事件
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void MyDocument_PrintPage(object sender, System.Drawing.Printing.PrintPageEventArgs e)
{
//页面大小
PaperSize pageSize = e.PageSettings.PaperSize;
//根据页面大小,按比例划分列宽
//序号
cellWidth[0] = (float)(pageSize.Width * 0.098);
//描述
cellWidth[1] = (float)(pageSize.Width * 0.36);
//当前值
cellWidth[2] = (float)(pageSize.Width * 0.2);
//出厂值
cellWidth[3] = (float)(pageSize.Width * 0.1176);
//计算列的起始位置
cellLeft[0] = e.PageSettings.Margins.Left;
cellLeft[1] = cellLeft[0] + cellWidth[0];
cellLeft[2] = cellLeft[1] + cellWidth[1];
cellLeft[3] = cellLeft[2] + cellWidth[2];
//高度
int tempTop = e.MarginBounds.Top;
//是否新的页面
bool tempIsNewPage = true;
//设置打印字符串的格式
StringFormat tempStrFormat = new StringFormat
{
Alignment = StringAlignment.Near,
LineAlignment = StringAlignment.Center,
Trimming = StringTrimming.EllipsisCharacter
};
//设置字体
Font printFont = new Font("宋体", 9);
//计算字体的高度
int fontHeight = (int)e.Graphics.MeasureString("KK00.10序号", printFont).Height;
//指定高质量、低速度呈现
e.Graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
try
{
string printStr;
while(printPoint < saveTable.Rows.Count)
{//逐行分页打印所有参数
if (tempTop + fontHeight > e.MarginBounds.Bottom)
{//判断当前打印是否超出页面范围,决定是否换页
//打印页脚
DrawFooter(e, PageNo);
PageNo++;
//继续分页打印
e.HasMorePages = true;
return;
}
//打印当前页的内容
//KK00.00
printStr = saveTable.Rows[printPoint][0].ToString().Substring(5);
if (printStr.Equals("00") || tempIsNewPage)
{//一组数据的开始
tempIsNewPage = false;
tempTop += 2;
//绘制标题
DrawTitle(e, tempTop, tempStrFormat);
tempTop += fontHeight;
}
//绘制内容
DrawContent(e, printPoint, tempTop, tempStrFormat);
//更新Y轴高度
tempTop += fontHeight;
//序号自增
printPoint++;
//判断是否已经到终点
if(printPoint >= saveTable.Rows.Count)
{
//绘制页脚
DrawFooter(e, PageNo);
//重置打印参数
PageNo = 1;
printPoint = 0;
return;
}
}
}
catch
{
}
}
/// <summary>
/// 绘制页脚
/// </summary>
/// <param name="e">绘图对象</param>
/// <param name="pageNo">页面序号</param>
private void DrawFooter(System.Drawing.Printing.PrintPageEventArgs e, int pageNo)
{
Font font = new Font("微软雅黑", 10);
//绘制直线
e.Graphics.DrawLine(new Pen(Color.Black, 1),
new Point(e.MarginBounds.Left, e.MarginBounds.Bottom),
new Point(e.MarginBounds.Right, e.MarginBounds.Bottom));
//公司信息标注
string tempStr = "Test for Windows(C) by 唠嗑一夏 Electric Corporation";
e.Graphics.DrawString(tempStr,
font, Brushes.Black,
e.MarginBounds.Left,
e.MarginBounds.Bottom+2);
//页脚序号
tempStr = pageNo.ToString();
e.Graphics.DrawString(tempStr,
font, Brushes.Black,
e.MarginBounds.Right - e.Graphics.MeasureString(tempStr, font).Width,
e.MarginBounds.Bottom+2);
}
/// <summary>
/// 绘制标题头
/// </summary>
/// <param name="e">绘图对象</param>
/// <param name="tempTop">Y轴坐标</param>
/// <param name="tempFormat">字符串格式</param>
private void DrawTitle(System.Drawing.Printing.PrintPageEventArgs e,
int tempTop, StringFormat tempFormat)
{
//绘制水平线
e.Graphics.DrawLine(Pens.Black,
new Point(e.MarginBounds.Left, tempTop),
new Point(e.MarginBounds.Right, tempTop));
Font printFont = new Font(fontName, fontNum);
//计算字体的高度 K00ARk, 根据使用到的字进行组合成串
int fontHeight = (int)(e.Graphics.MeasureString("K00ARk", printFont).Height);
//画刷
SolidBrush brush = new SolidBrush(Color.Black);
//填充背景色
e.Graphics.FillRectangle(Brushes.LightBlue,
new RectangleF(e.MarginBounds.Left, tempTop, e.MarginBounds.Width, fontHeight));
//列的序号
int col = 0;
//列 序号
string drawString = TitleStr[col];
//矩形框
RectangleF rectangle = new RectangleF((int)cellLeft[col], tempTop,
(int)cellWidth[col], fontHeight);
e.Graphics.DrawString(drawString, printFont,
brush, rectangle, tempFormat);
col++;
//列 名称
drawString = TitleStr[col];
//水平向右平移 并设置矩形框的宽度
rectangle.X = cellLeft[col];
rectangle.Width = cellWidth[col];
e.Graphics.DrawString(drawString, printFont,
brush, rectangle, tempFormat);
col++;
//列 设定值
drawString = TitleStr[col];
//水平向右平移 并设置矩形框的宽度
rectangle.X = cellLeft[col];
rectangle.Width = cellWidth[col];
e.Graphics.DrawString(drawString, printFont,
brush, rectangle, tempFormat);
col++;
//列 默认值
drawString = TitleStr[col];
//水平向右平移 并设置矩形框的宽度
rectangle.X = cellLeft[col];
rectangle.Width = cellWidth[col];
e.Graphics.DrawString(drawString, printFont,
brush, rectangle, tempFormat);
}
/// <summary>
/// 绘制内容
/// </summary>
/// <param name="e">绘图对象</param>
/// <param name="printIndex">数据行索引</param>
/// <param name="tempTop">Y轴坐标</param>
/// <param name="tempFormat">字符串格式</param>
private void DrawContent(System.Drawing.Printing.PrintPageEventArgs e,
int printIndex, int tempTop, StringFormat tempFormat)
{
Font printFont = new Font(fontName, fontNum);
//计算字体的高度 K00ARk, 根据使用到的字进行组合成串
int fontHeight = (int)(e.Graphics.MeasureString("K00ARk", printFont).Height);
//画刷
SolidBrush brush = new SolidBrush(Color.Black);
//列的序号
int col = 0;
//列 序号
string drawString = saveTable.Rows[printIndex][0].ToString() + saveTable.Rows[printIndex][1].ToString();
//矩形框
RectangleF rectangle = new RectangleF((int)cellLeft[col], tempTop,
(int)cellWidth[col], fontHeight);
e.Graphics.DrawString(drawString, printFont,
brush, rectangle, tempFormat);
col++;
//列 名称
drawString = saveTable.Rows[printIndex][3].ToString();
//水平向右平移 并设置矩形框的宽度
rectangle.X = cellLeft[col];
rectangle.Width = cellWidth[col];
e.Graphics.DrawString(drawString, printFont,
brush, rectangle, tempFormat);
col++;
//列 设定值
drawString = saveTable.Rows[printIndex][2].ToString();
//水平向右平移 并设置矩形框的宽度
rectangle.X = cellLeft[col];
rectangle.Width = cellWidth[col];
e.Graphics.DrawString(drawString, printFont,
brush, rectangle, tempFormat);
col++;
//列 默认值
drawString = saveTable.Rows[printIndex][4].ToString();
//水平向右平移 并设置矩形框的宽度
rectangle.X = cellLeft[col];
rectangle.Width = cellWidth[col];
e.Graphics.DrawString(drawString, printFont,
brush, rectangle, tempFormat);
}
}
}
最后的效果图
小节
1 使用PrintPreviewDialog + PrintDocument 可以实现打印预览功能
2 页面的绘制是在PrintDocument 的PrintDocument.PrintPage打印页事件中绘制
3 System.Drawing.Printing.PrintPageEventArgs e 在 e.Graphics的画布上进行绘制,DrawString绘制文本,DrawLine绘制直线,FillRectangle填充背景色,根据实际需要选择相应的接口。
4 绘制需要注意绘制点的X Y 坐标,可以以页面的边距e.MarginBounds作为参考点,使用e.Graphics.MeasureString可以测量该字符串占用的高和宽。