随心所欲的Web页面打印技术
一.概述
? 對(duì)基于B/S架構(gòu)的應(yīng)用程序而言,客戶端的頁(yè)面打印一直是比較頭疼的問(wèn)題,簡(jiǎn)單的做法是:1.使用IE的打印功能;2.使用水晶報(bào)表。但以上兩種辦法,都有很大的局限性,很難實(shí)現(xiàn)特殊要求的排版和精確的定位,所以不能滿足一些特殊客戶的BT要求。為此,本人總結(jié)了自己在使用Web打印上的一點(diǎn)經(jīng)驗(yàn),和大家分享。
? 本文涉及以下技術(shù):javascript、ActiveX、ASP.NET、GDI+。
二.基本架構(gòu)
? 首先,我們不能使用IE的打印功能,必須自己設(shè)計(jì)‘打印’按鈕。很多人習(xí)慣將‘打印’按鈕放在要打印的頁(yè)面上,打印時(shí)為了不把這個(gè)按鈕打印出來(lái),采用辦法如下:1.打印前隱藏按鈕;2.打印;3.顯示按鈕。
我覺得這樣比較麻煩,所以我采用框架。一共有三個(gè)頁(yè)面:
1.main.htm?:框架頁(yè)面,上面是打印按鈕,下面是要顯示的頁(yè)面。
2.header.htm?:標(biāo)題欄,至少包含一個(gè)打印按鈕。
3.report.aspx?:要打印的頁(yè)面,由用戶生成。
//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>
? 這樣,在點(diǎn)擊‘打印’按鈕時(shí),將直接打印report.aspx頁(yè)面的內(nèi)容,既簡(jiǎn)單又直觀。
三.打印
?要完全控制打印,就必須由程序設(shè)定頁(yè)眉、頁(yè)腳、頁(yè)邊距。每個(gè)客戶端的IE設(shè)置都不盡相同,你可以要求你的客戶修改他們的打印設(shè)置為你指定的值,顯然這不現(xiàn)實(shí)。所以正確的做法是:
1.備份客戶打印頁(yè)面設(shè)置;
2.設(shè)置頁(yè)眉頁(yè)腳上下左右邊距為自己需要的值;
3.打印;
4.恢復(fù)原來(lái)的打印頁(yè)面設(shè)置。
?這里用到一個(gè)叫ScriptX的控件。你需要下載一個(gè)文件:smsx.cab,下載地址:http://www.meadroid.com/scriptx/sxdownload.asp。這個(gè)地址并不能保證長(zhǎng)期有效,你可以在搜索引擎上搜索‘ScriptX’以獲得更多相關(guān)信息。
在“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指向你的實(shí)際存放文件的位置,在客戶端第一次瀏覽該頁(yè)面時(shí),將下載并安裝該控件,請(qǐng)確定客戶端的安全設(shè)置允許安裝控件。
那么此時(shí) header.htm 的內(nèi)容如下。
//header.htm
<html>
<head>
?<script id="clientEventHandlersJS" language="javascript">
?<!--
?function btnPrint_onclick()
?{
? //備份客戶打印機(jī)設(shè)置
?? 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;
?
?? //設(shè)置頁(yè)眉頁(yè)腳上下左右邊距?
?? 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()
?
?? //恢復(fù)原來(lái)的打印設(shè)置
?? 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>
? 此時(shí),不管客戶端IE設(shè)置如何,都能正確打印頁(yè)面,打印內(nèi)容將完全取決于頁(yè)面:report.aspx。
四.動(dòng)態(tài)頁(yè)面生成
1. 如果你的頁(yè)面是靜態(tài)頁(yè)面,或者頁(yè)面元素為固定數(shù)量,那就非常簡(jiǎn)單了。只要調(diào)整好各個(gè)元素位置就行了,絕對(duì)所見即所得。要注意的是一個(gè)A4紙的大小為21cm×29.7cm,對(duì)應(yīng)象素大約為 794×1123 (系統(tǒng)分辨率96DPI) 。
2.如果你要生成一些圖表,可以使用GDI+,你自己繪制的圖片絕對(duì)能滿足客戶需求。下面給一個(gè)例子,我在頁(yè)面放一個(gè)Label,一個(gè)Image。Image動(dòng)態(tài)生成,調(diào)整其位置使其打印時(shí)出現(xiàn)在第二頁(yè)的左上角。
?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?窗體設(shè)計(jì)器生成的代碼#region?Web?窗體設(shè)計(jì)器生成的代碼
?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
注意:要保證存放圖片的目錄,有寫權(quán)限。
3.以上技術(shù)只適合于頁(yè)面元素為固定數(shù)量的情況,對(duì)于頁(yè)面內(nèi)容大小不定的情況,例如,要打印一份人員信息的清單,人員數(shù)量為1~1000不等,每頁(yè)顯示20條記錄,要有規(guī)定的頁(yè)眉、頁(yè)腳,此時(shí)該如何處理。
? 思路:ASP.NET頁(yè)面都有一個(gè)基類System.Web.UI.Page,該類有一個(gè)保護(hù)方法叫void Render(HtmlTextWriter writer),就是通過(guò)這個(gè)方法,ASP.NET在后臺(tái)把WEB服務(wù)器端控件的屬性轉(zhuǎn)換成HTML代碼,并發(fā)送到客戶端供瀏覽器顯示。我們的辦法就是“劫持”該方法,完全手工生成所需頁(yè)面標(biāo)記代碼。
先看一個(gè)例子:
?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>");
??}
?}
? 編譯并運(yùn)行以上程序,可以看到一行一號(hào)字體的"Hello,world!",查看頁(yè)面源文件,內(nèi)容如下:
?<HTML><body><h1>Hello,world!</h1></body></html>
? 對(duì)照上面代碼,應(yīng)該非常好理解,下面我們就做一個(gè)實(shí)際的例子,將信息從數(shù)據(jù)庫(kù)讀出,顯示在頁(yè)面上,每頁(yè)顯示15條記錄。
??{
???writer.Write("<HTML>");
???writer.Write("<body>");??
???DataTable?tabRY?=?GetCustomerInfo();?//讀取數(shù)據(jù)庫(kù)
???int?Lines?=?15;????//每頁(yè)行數(shù)
???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>編號(hào)</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()?+"頁(yè),共"?+?
TotalPage.ToString()?+?"頁(yè)</div></td>");
???writer.Write("??</tr>");
???writer.Write("</table>");
??}
感覺又回到了用記事本做網(wǎng)頁(yè)的年代,手工生成HTML代碼,是不是真正叫“隨心所欲”。
幾點(diǎn)說(shuō)明:
(1)在每一頁(yè)(除了第一頁(yè))的頭部加入 writer.Write("<p? style=page-break-before:always></p>"); 目的是控制在打印時(shí),打印機(jī)在此換頁(yè)。這里通過(guò)強(qiáng)制打印機(jī)換頁(yè)來(lái)實(shí)現(xiàn)頁(yè)面的布局,與上面的絕對(duì)定位的辦法不同。該標(biāo)記只影響打印,不影響顯示。
(2)用記事本做網(wǎng)頁(yè)絕對(duì)很痛苦,而且HTML標(biāo)記也很不好用,我的辦法是:用Dreamweaver生成需要的頁(yè)面,再參照其HTML代碼進(jìn)行編程。
(3)盡量使用HtmlTextWriter類提供的一些其它方法如WriteBeginTag等取代Write方法,這樣可以提高頁(yè)面在客戶端的兼容性。同時(shí)在每個(gè)標(biāo)記后加入writer.WriteLine();進(jìn)行換行,以便于調(diào)試。
轉(zhuǎn)載于:https://www.cnblogs.com/Aioria0622/archive/2007/12/03/981434.html
總結(jié)
以上是生活随笔為你收集整理的随心所欲的Web页面打印技术的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: windows上编译zlib-1.2.8
- 下一篇: 万字干货,交互设计精髓105条设计原则(