转自博客园:随心所欲的Web页面打印技术
原文地址:http://www.cnblogs.com/seabluescn/archive/2006/11/23/569945.html
一.概述
对基于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动态生成,调整其位置使其打印时出现在第二页的左上角。
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条记录。
{
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();进行换行,以便于调试。
转自博客园:随心所欲的Web页面打印技术相关推荐
- 随心所欲的Web页面打印技术
一.概述 对基于B/S架构的应用程序而言,客户端的页面打印一直是比较头疼的问题,简单的做法是:1.使用IE的打印功能:2.使用水晶报表.但以上两种办法,都有很大的局限性,很难实现特殊要求的排版和精 ...
- 随心所欲的Web页面打印技术 20(转载)
四.动态页面生成 1. 如果你的页面是静态页面,或者页面元素为固定数量,那就非常简单了.只要调整好各个元素位置就行了,绝对所见即所得.要注意的是一个A4纸的大小为21cm×29.7cm,对应象素大约为 ...
- 博客园外挂之一Web搜索引擎命中统计
原来一直都觉得,把文章发到博客园首页会很大的提高自己blog的访问量.由于开始我只在blog上放了一个访问计数器,非常简单的一个计数器,只能计数,而且refresh都计数. 后来我又放了一个访问统计器 ...
- 博客园北京俱乐部第三次技术活动(2009/5/23)总结
在博客园各位园友的支持下,博客园北京俱乐部第三次技术交流活动在微软成功举行.本次活动共有40位园友参与,分别由四位朋友带来了4个精彩主题.最大的感谢送给我们免费提供场地的微软中国,以及提供礼品赞助的博 ...
- 博客园SimpleMemory皮肤的页面美化--感谢BNDong大佬
1. 前言说明 本博客的博皮样式设计者是BNDong大神,在此表示衷心的感谢!为了让更多人的博客园更加的美观大方,本人特此参考设计者BNDong关于博客样式的文章,写了这更加通俗易懂的文章,让更多喜欢 ...
- iOS_CNBlog项目开发 (基于博客园api开发) 上篇
按照惯例, 先上效果图 前言 做这个项目是因为刚好在逛博客园的时候看到一篇文章 博客园第三方客户端-i博客园正式发布App Store, 这里就帮忙贴下链接吧. 整个项目做下来大概做了半个月, 今天算 ...
- Asp.net MVC 仿照博客园的简单网站首页 列表设计
本来我打算采用ajax提交请求,异步的请求获取数据,但是我发现如果这样的话就会拖慢开发的进度,拖长时间.所以在这篇博客中仿照首页的列表设计其实和左侧列表网站分类采用了同样的方式,通过局部视图的方式呈现 ...
- 计算机技术博客博客知乎,我的技术博客的选择:CSDN、博客园、简书、知乎专栏仍是Github Page?...
有不少技术人员在学习到必定程度后发现了写博客的重要性,一方面帮助本身记忆,一方面也能帮助他人解决问题,因而会选择本身开始写博客,以后又发现平台太多不知从何下手,在这里我根据本身写博客的经验比较一下各个 ...
- 同样的文章2个网站2种待遇,2个眼光,博客园是逼我走、程序员网站是请我过去,这就是人才在不同公司的待遇一样的道理
昨天晚上很热血的写了两篇文章,结果一个被博客园强制撤下首页. 今天被CSDN网站发布到首页,为我免费做广告,http://www.csdn.net/, 同时给我上了2个首页. C# ASP.NET 开 ...
最新文章
- java学习笔记11--Annotation
- UNIX环境高级编程 第12章 线程控制
- C语言visual studio警告:取消对NULL指针“p”的引用
- 安装部署OpenStack(添加资源)
- 深度学习在处理视频上几种主要技术方法
- json 格式化工具_如何在命令行中优雅地处理JSON
- EBS并发管理器请求汇总(按照并发消耗时间,等待时间,平均等待事件等汇总)...
- java 纳秒 毫秒_golang的time包:秒、毫秒、纳秒时间戳输出方式
- java 释放数组_java集合ArrayList中clear方法内存释放分析
- python之强化学习入门
- 错误调试:GPU 版 TensorFlow failed to create cublas handle: CUBLAS_STATUS_ALLOC_FAILED
- 神舟刷蓝天w650dbios_Hasse神舟笔记本卡logo解决,刷BIOS方法,教你修复神船
- 盘点python socket 中recv函数的坑
- Android Studio kotlin代码莫名出现“lazy”,“arrayListOf”,“let”等关键字变红的问题
- java 设计连连看_软件设计之基于Java的连连看小游戏(一)——开题及游戏首页的制作...
- 服务器审计资质证书,利用ACS服务器实现用户的认证、授权和审计
- mysql统计没有参加考试的学生名单_sQL SERVER,帮我编写一个存储过程,查询没有参加考试的学生名单,要求显示姓名、学号,具体请补充:...
- 03-日志汇总,处理和安全分析
- Mac平台上有哪些好用的常用软件?
- opencv和pytorch中的warp操作函数:cv2.warpAffine, torch.nn.functional.grid_sample, cv2.warpPerspective