随心所欲的Web页面打印技术

一.概述
  对基于B/S架构的应用程序而言,客户端的页面打印一直是比较头疼的问题,简单的做法是:1.使用IE的打印功能;2.使用水晶报表。但以上两种办法,都有很大的局限性,很难实现特殊要求的排版和精确的定位,所以不能满足一些特殊客户的BT要求。为此,本人总结了自己在使用Web打印上的一点经验,和大家分享。
  本文涉及以下技术:javascript、ActiveX、ASP.NET、GDI+。

二.基本架构
  首先,我们不能使用IE的打印功能,必须自己设计‘打印’按钮。很多人习惯将‘打印’按钮放在要打印的页面上,打印时为了不把这个按钮打印出来,采用办法如下:1.打印前隐藏按钮;2.打印;3.显示按钮。
我觉得这样比较麻烦,所以我采用框架。一共有三个页面:
1.main.htm :框架页面,上面是打印按钮,下面是要显示的页面。
2.header.htm :标题栏,至少包含一个打印按钮。
3.report.aspx :要打印的页面,由用户生成。

//main.htm
<html>
 <head>
  <title></title>  
 </head> 
  <frameset rows="10%,90%" frameborder="0" border="0" frameSpacing="0">
  <frame id="header" name="header" src="Header.htm" noresize scrolling="no">
  <frame id="report" name="report" src="Report.aspx" noresize scrolling="auto">
 </frameset> 
</html>

//header.htm
<html>
 <head>  
  <script id=clientEventHandlersJS language=javascript>
  <!--
  function btnPrint_onclick()
  {   
     parent.report.focus();
     parent.report.print();  
  }
  //-->
  </script>
 </head>
 <body> 
  <INPUT id="btnPrint" type="button" value="Print" name="Print" οnclick="return btnPrint_onclick()">  
 </body>
</html>

  这样,在点击‘打印’按钮时,将直接打印report.aspx页面的内容,既简单又直观。

三.打印
 要完全控制打印,就必须由程序设定页眉、页脚、页边距。每个客户端的IE设置都不尽相同,你可以要求你的客户修改他们的打印设置为你指定的值,显然这不现实。所以正确的做法是:
1.备份客户打印页面设置;
2.设置页眉页脚上下左右边距为自己需要的值;
3.打印;
4.恢复原来的打印页面设置。

 这里用到一个叫ScriptX的控件。你需要下载一个文件:smsx.cab,下载地址:http://www.meadroid.com/scriptx/sxdownload.asp。这个地址并不能保证长期有效,你可以在搜索引擎上搜索‘ScriptX’以获得更多相关信息。

在“header.htm”中增加该控件的引用:
<OBJECT id="factory" style="DISPLAY: none" codeBase="http://localhost/WebApplication1/smsx.cab#VVersion=6,3,434,12"
 classid="clsid:1663ed61-23eb-11d2-b92f-008048fdd814" viewastext>
</OBJECT>
注意CodeBase指向你的实际存放文件的位置,在客户端第一次浏览该页面时,将下载并安装该控件,请确定客户端的安全设置允许安装控件。
那么此时 header.htm 的内容如下。

//header.htm
<html>
<head>
 <script id="clientEventHandlersJS" language="javascript">
 <!--
 function btnPrint_onclick()
 {
  //备份客户打印机设置
   var h = factory.printing.header;
   var f = factory.printing.footer;
   var t = factory.printing.topMargin;
   var b = factory.printing.bottomMargin;
   var l = factory.printing.leftMargin;
   var r = factory.printing.rightMargin;
 
   //设置页眉页脚上下左右边距 
   factory.printing.header = "";
    factory.printing.footer = "";
    factory.printing.topMargin="0";
   factory.printing.bottomMargin="0";
   factory.printing.leftMargin="0";
   factory.printing.rightMargin="0";
 
   //打印
   parent.report.focus();
   parent.report.print()
 
   //恢复原来的打印设置
   factory.printing.header = h;
   factory.printing.footer = f;
    factory.printing.topMargin=t;
   factory.printing.bottomMargin=b;
   factory.printing.leftMargin=l;
   factory.printing.rightMargin=r; 
 }
 //-->
 </script>
 
 <OBJECT id="factory" style="DISPLAY: none" codeBase="http://localhost/WebApplication1/smsx.cab#VVersion=6,3,434,12"
  classid="clsid:1663ed61-23eb-11d2-b92f-008048fdd814" viewastext>
 </OBJECT>
</head>
<body bgColor="#9999cc">
 <INPUT id="btnPrint" type="button" value="Print" name="Print" οnclick="return btnPrint_onclick()">   
</body>
</html>

  此时,不管客户端IE设置如何,都能正确打印页面,打印内容将完全取决于页面:report.aspx。

四.动态页面生成
1. 如果你的页面是静态页面,或者页面元素为固定数量,那就非常简单了。只要调整好各个元素位置就行了,绝对所见即所得。要注意的是一个A4纸的大小为21cm×29.7cm,对应象素大约为 794×1123 (系统分辨率96DPI) 。

2.如果你要生成一些图表,可以使用GDI+,你自己绘制的图片绝对能满足客户需求。下面给一个例子,我在页面放一个Label,一个Image。Image动态生成,调整其位置使其打印时出现在第二页的左上角。

 1public class Report : System.Web.UI.Page
 2 {
 3  protected System.Web.UI.WebControls.Image Image1;
 4  protected System.Web.UI.WebControls.Label Label1;
 5 
 6  Web 窗体设计器生成的代码#region Web 窗体设计器生成的代码
 7  override protected void OnInit(EventArgs e)
 8  {   
 9   InitializeComponent();
10   base.OnInit(e);
11  }

12  private void InitializeComponent()
13  {    
14   this.Load += new System.EventHandler(this.Page_Load);
15  }

16  #endregion

17
18  private void Page_Load(object sender, System.EventArgs e)
19  {   
20   if(!Page.IsPostBack)
21   {
22    InitImage();
23   }

24  }

25
26  private void InitImage()
27  {
28   Bitmap bmp = new Bitmap(800,1120);
29   Graphics g =  Graphics.FromImage(bmp);
30
31   g.FillRectangle(Brushes.Gray,0,0,800,1120);
32
33   g.FillRectangle(Brushes.RoyalBlue,0,0,100,600);
34   g.FillRectangle(Brushes.Aqua,600,0,100,600);
35   g.FillRectangle(Brushes.Coral,700,0,100,600); 
36 
37   g.FillRectangle(Brushes.YellowGreen,0,800,800,100);
38   g.FillRectangle(Brushes.Beige,0,900,800,100);
39   g.FillRectangle(Brushes.SkyBlue,0,1000,800,100);
40   g.FillRectangle(Brushes.Tomato,0,1100,800,20);
41
42   string filename = Server.MapPath("TempImages\\img1.jpg");   
43
44   bmp.Save(filename, System.Drawing.Imaging.ImageFormat.Jpeg);
45
46   this.Label1.Text = filename;
47   this.Image1.ImageUrl = filename;
48   this.Image1.Attributes["style"]="POSITION: absolute; LEFT: 0cm;  TOP: 29.7cm"//定位
49  }
  
50 }

51
52

注意:要保证存放图片的目录,有写权限。

3.以上技术只适合于页面元素为固定数量的情况,对于页面内容大小不定的情况,例如,要打印一份人员信息的清单,人员数量为1~1000不等,每页显示20条记录,要有规定的页眉、页脚,此时该如何处理。
  思路:ASP.NET页面都有一个基类System.Web.UI.Page,该类有一个保护方法叫void Render(HtmlTextWriter writer),就是通过这个方法,ASP.NET在后台把WEB服务器端控件的属性转换成HTML代码,并发送到客户端供浏览器显示。我们的办法就是“劫持”该方法,完全手工生成所需页面标记代码。
先看一个例子:
 public class FrmRYInfo : System.Web.UI.Page
 {
  private void Page_Load(object sender, System.EventArgs e)
  {   
  }
  protected override void Render(HtmlTextWriter writer)
  {
   writer.Write("<HTML>");
   writer.Write("<body>"); 
   writer.Write("<h1>Hello,world!</h1>");  
   writer.Write("</body>");
   writer.Write("</html>");
  }
 }
  编译并运行以上程序,可以看到一行一号字体的"Hello,world!",查看页面源文件,内容如下:
 <HTML><body><h1>Hello,world!</h1></body></html>
  对照上面代码,应该非常好理解,下面我们就做一个实际的例子,将信息从数据库读出,显示在页面上,每页显示15条记录。

 protected override void Render(HtmlTextWriter writer)
  
{
   writer.Write(
"<HTML>");
   writer.Write(
"<body>");  

   DataTable tabRY 
= GetCustomerInfo(); //读取数据库

   
int Lines = 15;    //每页行数
   int Count = tabRY.Rows.Count;
   
int TotalPage = Count/Lines + (Count%Lines==0?0:1);

   
for(int CurrentPage =0; CurrentPage<TotalPage; CurrentPage++)
   
{
    
int StartRow = CurrentPage * Lines;
    
int EndRow = StartRow + Lines;
    
if(EndRow > Count) EndRow = Count;

    ProcessCurrentPage(writer,tabRY,StartRow,EndRow,CurrentPage,TotalPage);
   }

  
   writer.Write(
"</body>");
   writer.Write(
"</html>");
  }


  
private void ProcessCurrentPage(HtmlTextWriter writer, DataTable tabRY, int StartRow, int EndRow, int 

CurrentPage, 
int TotalPage)
  
{
   
if(CurrentPage != 0)
   
{
    writer.Write(
"<p  style=page-break-before:always></p>");
              }

   
   writer.Write(
"<table  width=630 height=417 border=0>");
   writer.Write(
" <tr>");
   writer.Write(
"  <td width=624 height=47><div align=center  style=font-size:24px>人员信息汇总

</div></td>");
   writer.Write("  </tr>");
   writer.Write(
"  <tr>");
   writer.Write(
"  <td height=222>");

   writer.Write(
"   <table width=623  border=1 cellpadding=0 cellspacing=0>");
   writer.Write(
"   <tr>");
   writer.Write(
"    <td width=134><div align=center>姓名</div></td>");
   writer.Write(
"    <td width=134><div align=center>编号</div></td>");
   writer.Write(
"    <td width=134><div align=center>电话</div></td>");
   writer.Write(
"    <td width=134><div align=center>小灵通</div></td>");         
   writer.Write(
"   </tr>");

  
for(int i=StartRow; i<EndRow; i++)
  

   DataRow row 
= tabRY.Rows[i];
   
string XM = row["MC"].ToString();
   
string BH = row["BH"].ToString();
   
string DH = row["LXDH"].ToString();if(DH.Length==0)DH="-";
   
string XLT =  row["XLT"].ToString();

   writer.Write(
"   <tr>");
   writer.Write(
"    <td width=134><div align=center>" + XM + "</div></td>");
   writer.Write(
"    <td width=134><div align=center>" + BH + "</div></td>");
   writer.Write(
"    <td width=134><div align=center>" + DH + "</div></td>");
   writer.Write(
"    <td width=134><div align=center>" + XLT + "</div></td>");     

    
   writer.Write(
"   </tr>");
  }


   writer.Write(
"   </table>");

   writer.Write(
"  </td>");
   writer.Write(
"  </tr>");
   writer.Write(
"  <tr>");
   writer.Write(
"  <td height=37><div align=right>第" + (CurrentPage+1).ToString() +"页,共" + 

TotalPage.ToString() 
+ "页</div></td>");
   writer.Write(
"  </tr>");
   writer.Write(
"</table>");
  }


感觉又回到了用记事本做网页的年代,手工生成HTML代码,是不是真正叫“随心所欲”。
几点说明:
(1)在每一页(除了第一页)的头部加入 writer.Write("<p  style=page-break-before:always></p>"); 目的是控制在打印时,打印机在此换页。这里通过强制打印机换页来实现页面的布局,与上面的绝对定位的办法不同。该标记只影响打印,不影响显示。
(2)用记事本做网页绝对很痛苦,而且HTML标记也很不好用,我的办法是:用Dreamweaver生成需要的页面,再参照其HTML代码进行编程。
(3)尽量使用HtmlTextWriter类提供的一些其它方法如WriteBeginTag等取代Write方法,这样可以提高页面在客户端的兼容性。同时在每个标记后加入writer.WriteLine();进行换行,以便于调试。

转载于:https://www.cnblogs.com/Aioria0622/archive/2007/12/03/981434.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/269153.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

OAuth2.0学习(2-1)Spring Security OAuth2.0 开发指南

开发指南&#xff1a;http://www.cnblogs.com/xingxueliao/p/5911292.htmlSpring OAuth2.0 提供者实现原理&#xff1a;Spring OAuth2.0提供者实际上分为&#xff1a;授权服务 Authorization Service.资源服务 Resource Service.虽然这两个提供者有时候可能存在同一个应用程序中…

SQL的连接分为三种:内连接、外连接、交叉连接

建了两张表&#xff0c;学生表student 课程表 class 一、内连接&#xff1a; 内连接&#xff08;INNER JOIN&#xff09;&#xff1a;有两种&#xff0c;显式的和隐式的&#xff0c;返回连接表中符合连接条件和查询条件的数据行。&#xff08;所谓的链接表就是数据库在做查询形…

jion连接总结

1:内连接 2:左外连接 3:右外连接 4:左外连接 where判断 5:右外连接 where 6:全外连接 7:全外连接 where

福禄克网络电缆测试仪LinkIQ网络电缆测试仪的使用方法

作为福禄克官方授权经销商—明辰智航将给你一个完整的LinkIQ有线网络测试仪概述。首先&#xff0c;让我们来看看基本工具包里的内容。 我们有&#xff1a; LinkIQ本身 福禄克随身携带便捷包 快速参考指南说明书 CAT6a接线 远程ID 1用于完整的导线图测试 RJ45-to-RJ45模块…

步步为营-77-Ajax简介

AJax:异步JavaScript和XML.Asynchronous JavaScript and XML 优点:无刷新 1 JavaScript下的Ajax 1.1 XMLHttpRequest对象  使用ajax有一个很重要的对象XMLHttpRequest,而该对象的创建方式 var xhr new XMLHttpRequest();//常用 var xhr new ActiveXObject("Microsoft…

福禄克Link IQ功能:PoE负载测试及故障排除

自从最初的IEEE 802.3af 1型以太网供电&#xff08;PoE&#xff09;标准于2003年首次推出以来&#xff0c;该技术已经发展到包括2型&#xff08;高达30瓦&#xff09;、3型&#xff08;高达60瓦&#xff09;和4型&#xff08;高达90瓦&#xff09;。这意味着PoE电压现在支持从手…

利用局域网测试仪进行网络性能测试

随着互联网科技的飞速发展&#xff0c;网络变的越来越庞大&#xff0c;数据量越来越高&#xff0c;网络速率成几何倍数增长&#xff0c;网络应用也越来越复杂&#xff0c;对网络的性能、稳定性和可靠性都提出了更高的要求。在日益复杂的网络环境中一旦发生网络故障&#xff0c;…

详解-制作根文件系统,并使用yaffs,jffs,nfs挂载系统(2)

1.安装mkyaffsimage, mkyaffs2image命令(用来制作yaffs文件系统) 第一个命令针对Flash小页512B,第二个针对Flash大页2KB首先下载压缩文件 yaffs_source_util_larger_small_page_nand.tar.bz2安装mkyaffs2image命令为例: tar –xjf yaffs_source_util_larger_small_page_nand.ta…

数据中心综合布线时,使用交叉连接和互连的好处?

数据中心中&#xff0c;在交换机之间或交换机与服务器之间&#xff0c;为了灵活性和方便管理&#xff0c;有源设备之间都是使用交叉连接和通过配线架互联。在一些场合&#xff0c;还有的要求在同一通道内使用多个交叉连接或互联。 大多数人都知道&#xff0c;永久链路是通道的…

Oracle 通过出生日期计算年龄

sysdate 为系统日期时间&#xff0c;BIRTH_DATE 为表中出生日期字段 SELECTCOUNT( * ) FROMBARN_SWALLOW_INFO WHEREVALID_FLAG 1 AND IS_JINQIAO 1 AND TRUNC( months_between( SYSDATE, BIRTH_DATE ) / 12 ) > 25 AND TRUNC( months_between( SYSDATE, BIRTH_DATE )…

网站如何集成百度UEditor编辑器

在平时的网站维护使用过程中&#xff0c;富文本编辑器是网站必不可少的元素之一。现在市面上各种编辑器功能设计参差不齐&#xff0c;自己做了几个网站都是用蝉知建站系统做的&#xff0c;而蝉知默认内置的编辑器是KindEditor&#xff0c;功能简单&#xff0c;已经满足不了我的…

福禄克DSX系列教你如何测试铜缆跳线和光纤跳线

无论是用于在数据中心的光纤配线区和交换机之间进行连接的光纤跳线&#xff0c;还是用于将终端设备连接到工作区插座的局域网中的铜跳线&#xff0c;跳线都是网络不可或缺的一个组成部分&#xff0c;而跳线通常也是网络中最脆弱的环节。它比其他任何部件都更容易受到损坏。 由于…

通过福禄克LinkIQ网络电缆测试仪排除 VLAN 故障

IT行业的每个人都听说过局域网&#xff08;LAN&#xff09;&#xff0c;并且很可能知道它是一个由无数设备组成的网络——计算机、服务器、路由器、监控摄像头等他们都是都连接在一个物理位置。由于虚拟局域网&#xff08;VLAN&#xff09;在技术上并不存在于物理上&#xff0c…

2021-09-22

missing right parenthesis 少反括号 后面没跟反括号。

使用福禄克CFP单模光纤测试仪像专家一样设置参数!

在使用福禄克CFP单模光纤测试仪测试光纤链路时&#xff0c;如果要测试一个光纤接头的损耗&#xff0c;不可能只测量单个光纤接头&#xff0c;必须将其与类似的已知质量接头相匹配。这里光纤接头损耗指的是配对光纤接头的损耗&#xff0c;这也是为什么要设置参考值。 举例&…

java不要使用e.printStackTrace()

不要使用e.printStackTrace() 反例&#xff1a; 正例&#xff1a; 原因 e.printStackTrace()打印出的堆栈日志跟业务代码日志是交错混合在一起的&#xff0c;通常排查异常日志不太方便。e.printStackTrace()语句产生的字符串记录的是堆栈信息&#xff0c;如果信息太长太多&am…

阿里云服务器ECS按ctrl+alt+delete无法登录

今天在使用阿里云服务器远程桌面的时候发现怎么也进入不了&#xff0c;远程桌面无法连接&#xff0c;于是想到了在阿里云服务器管理控制台可以使用连接管理终端进行远程桌面连接&#xff0c;下面详细介绍阿里云服务器操作经验。 操作步骤如下 登录阿里云 - 进入云服务器管理控制…