废话少说,我们先来分析这个模板:
<!-- Template2.htm: 一个很小的模板,它支持打印
这个模板展示了一个最小的打印模板,他可以打印我们在预览时见到的内容。本模板与Template1.htm完全相同,只是增加了TEMPLATEPRINTER元素和一些支持打印的脚本。这些脚本以函数CheckPrint开始。该函数处理第二个LAYOUTRECT元素的onlayoutcomplete事件,由一个时间是100毫秒的定时器触发。CheckPrint检查dialogArguments.__IE_PrintType 属性来决定是否是预览文档,或者是在打印之前需要以一个打印对话框来提示用户,还是不提示用户而直接打印。使用定时器来延时非常重要,因为设备上下文(DC)在onlayoutcomplete时间没有完成之前,不能渲染打印机或者屏幕。延时让使得打印发生在onlayoutcomplete事件完成后再开始。1毫秒就足够让onlayoutcomplete时间完成,但为了安全起见,在本例中,把定时器设为100毫秒。
PrintPrep是一个重要的函数,因为设置了一个onreadystatechange处理函数,以确定模板的源文档在打印之前被完全装入。
-->
<HTML XMLNS:IE>
<HEAD>
<?IMPORT NAMESPACE="IE" IMPLEMENTATION="#default">
<STYLE TYPE="text/css">
.lorstyle
{
width:5.5in;
height:8in;
margin:1in;
background:white;
border:1 dashed gray;
}
.pagestyle
{
width:8.5in;
height:11in;
background:#FFFF99;
border-left:1 solid black;
border-top:1 solid black;
border-right:4 solid black;
border-bottom:4 solid black;
margin:10px;
}
</STYLE>
<SCRIPT LANGUAGE="JScript">
function CheckPrint()
{
switch (dialogArguments.__IE_PrintType)
{
case "Prompt":
if (printer.showPrintDialog())
PrintPrep();
break;
case "NoPrompt":
PrintPrep();
break;
case "Preview":
default:
break;
}
}
function PrintPrep()
{
if (layoutrect1.contentDocument.readyState == "complete")
{
//这一块会被调用当提示用户后再打印的时候,因为打印对话框会给出时间,
//让文档内容被完全装入。
PrintNow();
}
else
{
// This block will usually be called when printing w/o user prompt
// and sets an event handler that listens for the loading of the content
// document before printing. Sometimes, however, the content document
// will be loaded in time for the previous block to execute
layoutrect1.contentDocument.onreadystatechange = PrintWhenContentDocComplete;
}
}
function PrintWhenContentDocComplete()
{
if (layoutrect1.contentDocument.readyState == "complete")
{
layoutrect1.contentDocument.onreadystatechange = null;
PrintNow();
}
}
function PrintNow()
{
printer.startDoc("Printing from Tmplt2.htm");
printer.printPage(page1);
printer.printPage(page2);
printer.stopDoc();
}
</SCRIPT>
<IE:TEMPLATEPRINTER ID="printer"/>
</HEAD>
<BODY>
<IE:DEVICERECT ID="page1" CLASS="pagestyle" MEDIA="print">
<IE:LAYOUTRECT ID="layoutrect1" CONTENTSRC="document" CLASS="lorstyle" NEXTRECT="layoutrect2"/>
</IE:DEVICERECT>
<IE:DEVICERECT ID="page2" CLASS="pagestyle" MEDIA="print">
<IE:LAYOUTRECT ID="layoutrect2" CLASS="lorstyle" ONLAYOUTCOMPLETE="setTimeout('CheckPrint()', 100)"/>
</IE:DEVICERECT>
</BODY>
</HTML>
终于出现我们想要的打印框,按下打印,也还真的打印出来了。呵呵,心里非常高兴。那么奥妙在哪里呢?其实大家看了模板中的注释,已经比较清楚了,我再解释一下。
我们先来看CheckPrint函数,其代码如下:
function CheckPrint()
{
switch (dialogArguments.__IE_PrintType)
{
case "Prompt":
if (printer.showPrintDialog())
PrintPrep();
break;
case "NoPrompt":
PrintPrep();
break;
case "Preview":
default:
break;
}
}
大家可能都注意到了,该函数就是检查一个叫做dialogArguments对象的__IE_PrintType属性,然后调用相应的函数或者什么都不做,当__IE_PrintType的值为"Prompt"和"NoPrompt"的时候,就调用PrintPrep函数,而该函数就是进行打印。所以,关键就在dialogArguments对象,弄清楚了他,一切都明白了。
在IE下进行JS开发的人应该都知道,window对象也有一个属性叫做dialogArguments。他们会不会有什么关系呢?根据MSDN上说法,他们其实就是一回事,不过这个dialogArguments有一些局限,只能应用于打印模板。dialogArguments 不管是否向用户显示预览,他都是可用的,其中最重要的一个属性就是__IE_PrintType,他说明了打印模板是否应该向用户进行提示。
除了dialogArguments对象外,我们还接触到了一个很重要的事件,那就是ONLAYOUTCOMPLETE,他在打印或者预览版面在用源文档的内容完成了填充当前版面(layout)的时候触发,该事件还有一个至关重要的属性,那就是contentOverflow,如果为true,那么就表示文档还没有显示完,否则就是文档显示玩了,所以,我们只要在处理ONLAYOUTCOMPLETE事件的时候,根据contentOverflow的值进行判断,就知道需要多少个页面。
上面的例子尽管可以打印了,但还是不能根据文档内容生成适当的页面,下面的例子,我们就来一个可以根据文档内容动态获取页面数量的模板。
<!-- Template3.htm: 动态生成LayoutRect (仅可预览)
本模板演示了如何在源文档动态的装入模板的时候动态的生成LAYOUTRECT元素。第一个LAYOUTRECT在BODY元素的onload事件处理程序AddFirstPage中生成,其余的LAYOUTRECT在每个LAYOUTRECT的onlayoutcomplete事件处理函数——OnRectComplete——中生成。OnRectComplete检查event.contentOverflow属性,如果contentOverflow为true,函数为一个新的DEVICERECT和LAYOUTRECT元素生成HTML。一旦HTML被生成,OnRectComplete就会使用insertAjacentHTML方法把新的HTML插入到pageContainer这个DIV中。在新的LAYOUTRECT被插入后,模板可以继续装载模板的源文档。注意OnRectComplete也把当前LAYOUTRECT的onlayoutcomplete事件处理句柄设为null.这样就阻止了文档在重新装入的时候——例如窗口的大小被更改的时候——调用OnRectComplete。
本模板仅支持打印预览。
-->
<HTML XMLNS:IE>
<HEAD>
<?IMPORT NAMESPACE="IE" IMPLEMENTATION="#default">
<STYLE TYPE="text/css">
.lorstyle
{
width:5.5in;
height:8in;
margin:1in;
background:white;
border:1 dashed gray;
}
.pagestyle
{
background:#FFFF99;
border-left:1 solid black;
border-top:1 solid black;
border-right:4 solid black;
border-bottom:4 solid black;
width:8.5in;
height:11in;
margin:10px;
overflow:hidden;
}
</STYLE>
<SCRIPT LANGUAGE="JScript">
var iNextPageToCreate = 1;
function AddFirstPage()
{
newHTML = "<IE:DEVICERECT ID='page1' MEDIA='print' CLASS='pagestyle'>";
newHTML += "<IE:LAYOUTRECT ID='layoutrect1' CONTENTSRC='document' ONLAYOUTCOMPLETE='OnRectComplete()' EXTRECT='layoutrect2' CLASS='lorstyle'/>";
newHTML += "</IE:DEVICERECT>";
pagecontainer.insertAdjacentHTML("afterBegin", newHTML);
iNextPageToCreate = 2;
}
function OnRectComplete()
{
if (event.contentOverflow == true)
{
document.all("layoutrect" + (iNextPageToCreate - 1)).onlayoutcomplete = null;
newHTML = "<IE:DEVICERECT ID='page" + iNextPageToCreate + "' MEDIA='print' CLASS='pagestyle'>";
newHTML += "<IE:LAYOUTRECT ID='layoutrect" + iNextPageToCreate + "' ONLAYOUTCOMPLETE='OnRectComplete()' EXTRECT='layoutrect" + (iNextPageToCreate + 1) + "' CLASS='lorstyle'/>";
newHTML += "</IE:DEVICERECT>";
pagecontainer.insertAdjacentHTML("beforeEnd", newHTML);
iNextPageToCreate++;
}
}
</SCRIPT>
</HEAD>
<BODY ONLOAD="AddFirstPage()">
<DIV ID="pagecontainer">
<!--在这里动态的生成页面。 -->
</DIV>
</BODY>
</HTML>
在Document Address里输入保存有上述代码的htm文档的名字,然后单击Preview,效果如下图:
博客园的首页在打印模板中的页数是16页(这个数子当然不正确)。
我们的打印模板现在可以根据被打印内容动态的生成页面了,可是还是有许多的内容没有完成,比如设置正确的纸张大小,设置页边距,设置页眉和页脚等。在下一篇文章,我们继续讨论这些内容。
另外,老是用博客园的首页作为例子,不知DUDU有没有意见,如果有的话,就跟我说一声啊(^_^)。
欢迎大家拍砖。