.Net C# 打印功能学习笔记
参考文档地址:https://blog.csdn.net/dzweather/article/details/10171105
目标:打印一些复杂度较低的页面,使用.Net自带的类解决。
一、打印文件类:class PrintDocument
首先创建一个打印文件实例:var doc = new PrintDocument();
用于输出到打印机的打印文件内容,包含打印设置、所有绘制好的页面、以及页面设置。
当所有下述设计工作完成后,可以使用doc.Print()方法进行打印,也可以让用户自行在打印预览窗口里进行打印。
二、打印设置类:class PrinterSettings
可以让用户使用class PrintDialog进行设置,也可以在代码里设定,实现自动化打印。
a) 使用PrintDialog:
/// <summary>
/// 进行打印机设置
/// </summary>
public DialogResult PrinterSetting(Form owner = null)
{
var pd = new PrintDialog();
pd.Document = doc;
pd.AllowSomePages = true;
pd.AllowCurrentPage = true;
pd.AllowSelection = true;
DialogResult r;
if (owner == null)
r = pd.ShowDialog();
else
r = pd.ShowDialog(owner);
if (r != DialogResult.OK)
OnCancel();
return r;
}
用户点击确认后,会更新doc.PrinterSettings引用的PrinterSettings实例;否则引发OnCancel事件。
b) 使用代码设定:
var ps = doc.PrinterSettings;
ps.PrinterName = "..."; //打印机名称
//其他设置...
三、页面设置类:class PaperSettings
同样也可以使用两种方式来进行设置。
a) 使用PageSetupDialog:
/// <summary>
/// 进行页面设置
/// </summary>
public DialogResult PaperSetting(Form owner = null)
{
var ps = new PageSetupDialog();
ps.Document = doc;
ps.AllowPrinter = true;
ps.EnableMetric = true;
ps.AllowPaper = true;
ps.AllowMargins = true;
DialogResult r;
if (owner == null)
r = ps.ShowDialog();
else
r = ps.ShowDialog(owner);
if (r != DialogResult.OK)
OnCancel();
return r;
}
用户点击确认后,会更新doc.DefaultPageSettings引用的PageSettings实例;否则会引发OnCancel事件。
b) 使用代码设定:
var pas = doc.DefaultPageSettings;
pas.Margins.Left = 39;//左页边距,单位百分之一英寸
//其他设置...
四、打印预览类:class PrintPreviewDialog
.Net自带的打印预览窗口比较简陋,尤其不能忍的是上一页、下一页太难点了,这里我增加了两个用于切换页的按钮。
要注意的是,这个页不是打印时的页,只是在预览窗体里显示的页,也就是说,页数取决于打印总页数n和当前窗口预览页数m,那么预览的总页数就是n/m。比如总共6页,同时预览2页,那么打印预览的页就是共3页。
同时增加页面设置、打印机设置两个按钮,允许用户在查看打印预览时,修改打印设置。
/// <summary>
/// 进行打印预览
/// </summary>
public void Preview(Form owner = null)
{
var pd = new PrintPreviewDialog();
PreviewAddButton(pd);
pd.Document = doc;
if (owner == null)
pd.ShowDialog();
else
pd.ShowDialog(owner);
}
/// <summary>
/// 为打印预览窗口增加上一页、下一页、页面设置、打印机设置按钮
/// </summary>
/// <param name="pd"></param>
private void PreviewAddButton(PrintPreviewDialog pd)
{
var toolstrip = pd.Controls.OfType<ToolStrip>().First();
var next = new ToolStripButton();
next.Text = "下一页";
next.Click += new EventHandler((x, y) =>
{
var a = pd.PrintPreviewControl.StartPage;
pd.PrintPreviewControl.StartPage++;
if (pd.PrintPreviewControl.StartPage == a)
MessageBox.Show("已经是最后一页啦!", "没有下一页", MessageBoxButtons.OK, MessageBoxIcon.Information);
});
var back = new ToolStripButton();
back.Text = "上一页";
back.Click += new EventHandler((x, y) =>
{
var a = pd.PrintPreviewControl.StartPage - 1;
if (a >= 0)
pd.PrintPreviewControl.StartPage = a;
else
MessageBox.Show("已经是第一页啦!", "没有上一页", MessageBoxButtons.OK, MessageBoxIcon.Information);
});
var paperSet = new ToolStripButton();
paperSet.Text = "页面设置";
paperSet.Click += new EventHandler((x, y) =>
{
var r = PaperSetting();
if (r == DialogResult.OK)//点击确认后更新打印预览
pd.PrintPreviewControl.InvalidatePreview();
});
var printSet = new ToolStripButton();
printSet.Text = "打印机设置";
printSet.Click += new EventHandler((x, y) =>
{
var r = PrinterSetting();
if (r == DialogResult.OK)//点击确认后更新打印预览
pd.PrintPreviewControl.InvalidatePreview();
});
toolstrip.Items.Add(back);
toolstrip.Items.Add(next);
toolstrip.Items.Add(paperSet);
toolstrip.Items.Add(printSet);
pd.PrintPreviewControl.MouseWheel += PrintPreview_MouseWheel;//可以使用鼠标滚轮翻页
}
/// <summary>
/// 打印预览区域鼠标滚轮事件订阅器
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void PrintPreview_MouseWheel(object sender, MouseEventArgs e)
{
if (sender is PrintPreviewControl ct)
{
if (e.Delta > 0)
{
var a = ct.StartPage - 1;
if (a >= 0)
ct.StartPage = a;
}
else
{
ct.StartPage++;
}
}
}
五、页面的打印/打印预览事件:PrintPageEventHandler PrintPage(object sender, PrintPageEventArgs e)
在这个事件的订阅器中,编写打印输出页面绘制的代码。
下面这个是我的条码打印页面绘制方法:
/// <summary>
/// 打印条码时的每页打印方法
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void Barcode_PrintPage(object sender, PrintPageEventArgs e)
{
//当前页的二维码Image
var ncode = BarcodeList[NowPage - 1];
//可打印区域的左边界坐标
int left = e.MarginBounds.Left;
//可打印区域的右边界坐标
int right = e.MarginBounds.Right;
//可打印区域的上边界坐标
int top = e.MarginBounds.Top;
//可打印区域的下边界坐标
int bottom = e.MarginBounds.Bottom;
//可打印区域的总高度
int height = bottom - top;
//可打印区域的总宽度
int width = right - left;
//页眉高度
int headerHeight = Convert.ToInt32(Math.Ceiling(HeaderFont.GetHeight(e.Graphics)));
//页脚高度
int footerHeight = Convert.ToInt32(Math.Ceiling(FooterFont.GetHeight(e.Graphics)));
//创建页眉矩形区域
var headerR = new Rectangle(left, top, width, headerHeight);
//创建主体矩形区域
var bodyR = new Rectangle(left, headerR.Bottom, width, height - headerR.Height - footerHeight);
//创建页脚矩形区域
var footerR = new Rectangle(left, bodyR.Bottom, width, footerHeight);
//绘制三个区域的边框
e.Graphics.DrawRectangles(Pens.Black, new Rectangle[] { headerR, bodyR, footerR });
//绘制页眉文字
CenterText(e.Graphics, HeaderText[NowPage - 1], HeaderFont, Brushes.Black, headerR);
//绘制页脚文字
CenterText(e.Graphics, FooterText[NowPage - 1], FooterFont, Brushes.Black, footerR);
//原始二维码宽度
var cwidth = ncode.Width;
//原始二维码高度
var cheight = ncode.Height;
//缩放后的二维码宽度
var crwidth = width - MillimetertoCentinch(20f);
//缩放后的二维码高度
var crheight = Convert.ToInt32(Math.Ceiling(crwidth * 1.0 / cwidth * cheight));
//二维码绘制矩形区域,控制其与主体的边距为10mm,并等比例缩放
var codeR = new Rectangle(left + MillimetertoCentinch(10f), bodyR.Top + Convert.ToInt32(Math.Ceiling((bodyR.Height - crheight) * 1.0 / 2)), crwidth, crheight);
//绘制二维码
e.Graphics.DrawImage(ncode, codeR);
//页码计数加1
NowPage++;
//判断是否还有页
if (NowPage > TotalPage)
e.HasMorePages = false;
else
e.HasMorePages = true;
}
/// <summary>
/// 在指定绘图区域的指定矩形区域绘制居中的文本
/// </summary>
/// <param name="g">绘图区域</param>
/// <param name="t">要绘制的字符串</param>
/// <param name="f">字体对象</param>
/// <param name="b">填充对象</param>
/// <param name="rect">矩形区域对象</param>
private void CenterText(Graphics g, string t, Font f, Brush b, RectangleF rect)
{
var sf = new StringFormat();
sf.Alignment = StringAlignment.Center;
g.DrawString(t, f, b, rect, sf);
}
/// <summary>
/// 毫米转英寸
/// </summary>
/// <param name="millimeter">毫米</param>
/// <returns></returns>
public float MillimetertoInch(float millimeter)
{
return millimeter / 25.4f;
}
/// <summary>
/// 毫米转百分之一英寸
/// </summary>
/// <param name="millimeter">毫米</param>
/// <returns></returns>
public int MillimetertoCentinch(float millimeter)
{
return Convert.ToInt32(MillimetertoInch(millimeter) * 100f);
}
当然了,需要先注册这个订阅器:
doc.PrintPage += Barcode_PrintPage;
系统在打印/打印预览时,会一直调用这个方法,直到e.HasMorePages为False。
这里的NowPage和TotalPage,是写在类级别的公共属性,用于计算当前页和总页数。
NowPage需要在doc.BeginPrint事件的订阅器里设为1:
doc.BeginPrint += Begin_Print;
/// <summary>
/// 开始打印事件订阅器
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void Begin_Print(object sender, PrintEventArgs e)
{
NowPage = 1;
}
TotalPage则是在类的构造函数里计算,例如:
class MyPrinter
{
public delegate void PrintCancelHandler(); public event PrintCancelHandler OnCancel;
/// <summary>
/// 打印主对象
/// </summary>
public PrintDocument doc { get; set; }
/// <summary>
/// 条码List
/// </summary>
List<Image> _barcodes;
/// <summary>
/// 条码List
/// </summary>
public List<Image> BarcodeList
{
get
{
return _barcodes;
}
set
{
_barcodes = value;
}
}
/// <summary>
/// 总页数
/// </summary>
public int TotalPage { get; set; }
/// <summary>
/// 当前打印页数
/// </summary>
public int NowPage { get; set; }
/// <summary>
/// 页眉字体
/// </summary>
public Font HeaderFont { get; set; }
/// <summary>
/// 页脚字体
/// </summary>
public Font FooterFont { get; set; }
/// <summary>
/// 主体字体
/// </summary>
public Font BodyFont { get; set; }
/// <summary>
/// 页眉文本
/// </summary>
public List<string> HeaderText { get; set; }
/// <summary>
/// 页脚文本
/// </summary>
public List<string> FooterText { get; set; }
public MyPrinter(List<Image> barcodes)
{
doc = new PrintDocument();
BarcodeList = barcodes;
//条码总页数
TotalPage = BarcodeList.Count;
//注册条码打印的订阅器
doc.PrintPage += Barcode_PrintPage;
doc.BeginPrint += Begin_Print; this.OnCancel += DefaultCancel;
HeaderText = new List<string>();
FooterText = new List<string>();
for (int i = 0; i < TotalPage; i++)
{
//设置页眉、页脚文本
}
//页眉页脚字体
HeaderFont = new Font("微软雅黑", 9);
FooterFont = new Font("微软雅黑", 9);
}
protected virtual void DefaultCancel()
{ }
}
可以使用每页的Graphics对象,对当前页进行任意的绘制,DrawString,DrawImage,等等,.Net GDI+的使用网上资源很多,这里就不展开了。
总之,使用.Net打印的过程,过程是:详细设置=>绘制页面=>预览/打印。
其中绘制页面的动作发生在预览/打印动作发生之时。
如果需要做一些复杂的报表,当然还是水晶报表之类的扩展更为快捷。

浙公网安备 33010602011771号