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

随心所欲的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

 
    
  
  
  
  
  

//header.htm

   
  
 
  
    
 

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

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

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

在“header.htm”中增加该控件的引用:

注意CodeBase指向你的实际存放文件的位置,在客户端第一次浏览该页面时,将下载并安装该控件,请确定客户端的安全设置允许安装控件。
那么此时 header.htm 的内容如下。

//header.htm


 
 
 


    

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

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

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

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

12InBlock.gif  private void InitializeComponent()
13ExpandedSubBlockStart.gifContractedSubBlock.gif  dot.gif{    
14InBlock.gif   this.Load += new System.EventHandler(this.Page_Load);
15ExpandedSubBlockEnd.gif  }

16ExpandedSubBlockEnd.gif  #endregion

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

24ExpandedSubBlockEnd.gif  }

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

51 None.gif
52 None.gif

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

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("");
   writer.Write(""); 
   writer.Write("

Hello,world!

");  
   writer.Write("");
   writer.Write("");
  }
 }
  编译并运行以上程序,可以看到一行一号字体的"Hello,world!",查看页面源文件,内容如下:
 

Hello,world!


  对照上面代码,应该非常好理解,下面我们就做一个实际的例子,将信息从数据库读出,显示在页面上,每页显示15条记录。

None.gif   protected   override   void  Render(HtmlTextWriter writer)
ExpandedBlockStart.gifContractedBlock.gif  
dot.gif {
InBlock.gif   writer.Write(
"");
InBlock.gif   writer.Write(
"");  
InBlock.gif
InBlock.gif   DataTable tabRY 
= GetCustomerInfo(); //读取数据库
InBlock.gif

InBlock.gif   
int Lines = 15;    //每页行数
InBlock.gif
   int Count = tabRY.Rows.Count;
InBlock.gif   
int TotalPage = Count/Lines + (Count%Lines==0?0:1);
InBlock.gif
InBlock.gif   
for(int CurrentPage =0; CurrentPage<TotalPage; CurrentPage++)
ExpandedSubBlockStart.gifContractedSubBlock.gif   
dot.gif{
InBlock.gif    
int StartRow = CurrentPage * Lines;
InBlock.gif    
int EndRow = StartRow + Lines;
InBlock.gif    
if(EndRow > Count) EndRow = Count;
InBlock.gif
InBlock.gif    ProcessCurrentPage(writer,tabRY,StartRow,EndRow,CurrentPage,TotalPage);
ExpandedSubBlockEnd.gif   }

InBlock.gif  
InBlock.gif   writer.Write(
"");
InBlock.gif   writer.Write(
"");
ExpandedBlockEnd.gif  }

None.gif
None.gif  
private   void  ProcessCurrentPage(HtmlTextWriter writer, DataTable tabRY,  int  StartRow,  int  EndRow,  int  
None.gif
None.gifCurrentPage, 
int  TotalPage)
ExpandedBlockStart.gifContractedBlock.gif  
dot.gif {
InBlock.gif   
if(CurrentPage != 0)
ExpandedSubBlockStart.gifContractedSubBlock.gif   
dot.gif{
InBlock.gif    writer.Write(
"

");
ExpandedSubBlockEnd.gif              }

InBlock.gif   
InBlock.gif   writer.Write(
"");
InBlock.gif   writer.Write(
" ");
InBlock.gif   writer.Write(
"  人员信息汇总
InBlock.gif

InBlock.gif
div>td>");
InBlock.gif
   writer.Write("  ");
InBlock.gif   writer.Write(
"  ");
InBlock.gif   writer.Write(
"  ");
InBlock.gif
InBlock.gif   writer.Write(
"   ");
InBlock.gif   writer.Write(
"   ");
InBlock.gif   writer.Write(
"    姓名
");
InBlock.gif   writer.Write(
"    编号
");
InBlock.gif   writer.Write(
"    电话
");
InBlock.gif   writer.Write(
"    小灵通
");         
InBlock.gif   writer.Write(
"   ");
InBlock.gif
InBlock.gif  
for(int i=StartRow; i<EndRow; i++)
ExpandedSubBlockStart.gifContractedSubBlock.gif  
dot.gif
InBlock.gif   DataRow row 
= tabRY.Rows[i];
InBlock.gif   
string XM = row["MC"].ToString();
InBlock.gif   
string BH = row["BH"].ToString();
InBlock.gif   
string DH = row["LXDH"].ToString();if(DH.Length==0)DH="-";
InBlock.gif   
string XLT =  row["XLT"].ToString();
InBlock.gif
InBlock.gif   writer.Write(
"   ");
InBlock.gif   writer.Write(
"    " + XM + "
");
InBlock.gif   writer.Write(
"    " + BH + "
");
InBlock.gif   writer.Write(
"    " + DH + "
");
InBlock.gif   writer.Write(
"    " + XLT + "
");     
InBlock.gif
InBlock.gif    
InBlock.gif   writer.Write(
"   ");
ExpandedSubBlockEnd.gif  }

InBlock.gif
InBlock.gif   writer.Write(
"   ");
InBlock.gif
InBlock.gif   writer.Write(
"  ");
InBlock.gif   writer.Write(
"  ");
InBlock.gif   writer.Write(
"  ");
InBlock.gif   writer.Write(
"  " + (CurrentPage+1).ToString() +"页,共" + 
InBlock.gif
InBlock.gifTotalPage.ToString() 
+ "
");
InBlock.gif   writer.Write(
"  ");
InBlock.gif   writer.Write(
"");
ExpandedBlockEnd.gif  }

None.gif
None.gif

感觉又回到了用记事本做网页的年代,手工生成HTML代码,是不是真正叫“随心所欲”。
几点说明:
(1)在每一页(除了第一页)的头部加入 writer.Write("

"); 目的是控制在打印时,打印机在此换页。这里通过强制打印机换页来实现页面的布局,与上面的绝对定位的办法不同。该标记只影响打印,不影响显示。
(2)用记事本做网页绝对很痛苦,而且HTML标记也很不好用,我的办法是:用Dreamweaver生成需要的页面,再参照其HTML代码进行编程。
(3)尽量使用HtmlTextWriter类提供的一些其它方法如WriteBeginTag等取代Write方法,这样可以提高页面在客户端的兼容性。同时在每个标记后加入writer.WriteLine();进行换行,以便于调试。

 

 

 

转载于:https://www.cnblogs.com/seabluescn/archive/2006/11/23/569945.html

你可能感兴趣的:(随心所欲的Web页面打印技术)