Web打印是开发B/S系统无法回避的问题,这个问题往往客户很难理解为什么这么难,但是确实是开发人员心中的痛。当前Web打印的实现方式有好多种,归纳一下:
1.导出Execel,并且利用Excel的打印功能来实现
2.直接调用Web浏览器的打印方法进行页面区域的打印
3.采用第三方的打印控件进行打印
Web打印是开发B/S系统无法回避的问题,这个问题往往客户很难理解为什么这么难,但是确实是开发人员心中的痛。当前Web打印的实现方式有好多种,归纳一下:
1.导出Execel,并且利用Excel的打印功能来实现
2.直接调用Web浏览器的打印方法进行页面区域的打印
3.采用第三方的打印控件进行打印
应该来讲,第一种和第二种方式实现都相对比较简单,而往往也是大量报表打印的解决方案。但是缺陷也很明显,对于那些需要精确控制打印位置,批量打印,交互式打印效果却不是很理想。第三种方式可以避免上面的问题,但是往往需要ActiveX控件的支持,所以也存在一定的缺陷。
我们开发的这个项目中有需要打印证件的需求,是使用的针式打印机,刚开始采用了第一种方法,客户对于这种方案不满意,所以我们就开发了一个ActiveX控件来进行证件打印。
1、需求概述
系统在页面中列出所有需要打印证件的人员信息,用户选择其中的一个或者几个进行打印。每个打印完成后,将打印完成标记填写到数据库中去。
2、实现的基本思路
基于以上的需求,我们最终的解决思路如下:
a) 利用asp.net 的 WebMethord 从服务器提取人员信息
b) 将人员信息转化为打印XML文本,并且传递给客户端的ActiveX控件
c) 客户端的控件将接收的XML内容发送到打印机
所以这里面比较关键的有几个内容:
a) 在服务器端生成的XML文本的格式是怎样的。(XML文本中关键内容非常简单,页面是如何设置的?打印的内容是什么,这个内容打印到页面的什么位置!)
b) ActiveX控件如何取得服务器端生成的XML文本。 (由于传入的是XML文本,所以只需要这个控件能够解释XML文本就可以了,具体什么语言无所谓)
3、关键代码
我生成的xml文本的格式如下:

Code
<?xml version="1.0" encoding="GB2312"?>
<Report name="CertificatePrint">
<Page id="0">
<TextObject Name="发证日期" TextBrush="System.Drawing.SolidBrush" Text="2009年07月13日" Font="宋体,14,style=Regular" X="112" Y="584" />
<TextObject Name="证书编号" TextBrush="System.Drawing.SolidBrush" Text="09041050000004" Font="宋体,14,style=Regular" X="140" Y="660" />
<TextObject Name="姓名" TextBrush="System.Drawing.SolidBrush" Text="臧云停" Font="宋体,14,style=Regular" X="560" Y="300" />
<TextObject Name="性别" TextBrush="System.Drawing.SolidBrush" Text="女" Font="宋体,14,style=Regular" X="560" Y="348" />
<TextObject Name="职称" TextBrush="System.Drawing.SolidBrush" Text="" Font="宋体,14,style=Regular" X="560" Y="412" />
</Page>
</Report>
我们的需求中只有文本的打印,所以我这边都是用TextObject标签来实现了,如果您想打印线条或者图片等,再加上相关的支持就可以了。
<Page>标签是指每页包含的内容。
<TextObject>标签是指打印的文本,其中的TextBrush是刷子的定义,Text是指打印出的内容,Font对应的是打印字体,xy就是打印的坐标了。
这样看一下,大家就基本明白了打印XML的内容,接下来就要编写一个ActiveX控件了,这个控件的功能非常简单,只需要能够解释XML文本,并且将文本输出到指定的打印机就可以了。

Code
// 打印相关的私有变量
readonly PrintDocument printDocument = new PrintDocument();
private int i = 0;
private XmlNodeList pageNodes;
private int pageCount = 0;
public string printContent;
// 控件的构造函数中需要声明的事件
printDocument.BeginPrint += new PrintEventHandler(printDocument_BeginPrint);
printDocument.PrintPage += new PrintPageEventHandler(printDocument_PrintPage);
printDocument.EndPrint += new PrintEventHandler(printDocument_EndPrint);
//
#region 打印处理函数
void printDocument_EndPrint(object sender, PrintEventArgs e)
{
pageCount = 0;
}
void printDocument_BeginPrint(object sender, PrintEventArgs e)
{
if (string.IsNullOrEmpty(printContent))
{
e.Cancel = true;
}
XmlDocument xmlDocument = new XmlDocument();
xmlDocument.LoadXml(printContent);
pageNodes = xmlDocument.GetElementsByTagName("Page");
}
void printDocument_PrintPage(object sender, PrintPageEventArgs e)
{
if (pageCount < pageNodes.Count)
{
foreach (XmlNode xmlNode in pageNodes[pageCount].ChildNodes)
{
string text = xmlNode.Attributes["Text"].Value;
string font = xmlNode.Attributes["Font"].Value;
TypeConverter tc = TypeDescriptor.GetConverter(typeof(Font));
Font printFont = (Font)tc.ConvertFromString(font);
float x = Convert.ToSingle(xmlNode.Attributes["X"].Value);
float y = Convert.ToSingle(xmlNode.Attributes["Y"].Value);
e.Graphics.DrawString(text, printFont, Brushes.Black, x, y);
}
pageCount++;
// 判断是否继续
if (pageCount < pageNodes.Count)
{
e.HasMorePages = true;
}
else
{
e.HasMorePages = false;
}
}
}
public void Print()
{
PrintDialog printDialog = new PrintDialog();
printDialog.Document = printDocument;
if (printDialog.ShowDialog() == DialogResult.OK)
{
printDocument.Print();
}
}
#endregion
大家看一下,也是非常简单的几段代码。
最后一块内容就是如何调用的问题了,我们通过js调用WebMethod生成XML文本,并且将XML文本传递给ActiveX控件就一切OK了。

Code
// 打印
function cardPrint(){
if(ConfirmPrint()){
// 得到选中的checkbox,并且调用PageMethods来与后台进行数据交互
var values = GetSelectedRowValues('cbx_select');
PageMethods.GetPrintXml(values, HelloResult);
}
}
// 执行打印
function HelloResult(result){
try{
hello = new ActiveXObject("Train.Reg.Demo");
}catch(err){
alert('您可能没有正确的安装打印组件,或者浏览器设置不正确!\n\n错误信息为:' + err.description + '\n\n有关设置的详细信息请参考帮助!');
}
// result就是XML文本,将文本传递给ActiveX控件
hello.printContent = result;
hello.Print();
}