RDLC报表打印

来源:http://www.cnblogs.com/waxdoll/archive/2006/09/13/rdlc-report-in-web-application.html  
 不断地有人在我的Blog上提出这样的问题——为什么在Web窗体中使用.rdlc报表时,ReportViewer控件上没有“打印”按钮?在WEB窗体的设计状态,默认情况下,ReportViewer控件上是没有“打印”按钮的;如果在“ReportViewer 任务”窗口中选择一个客户端报表(一个.rdlc文件),也是没有“打印”按钮的;如果在“ReportViewer 任务”窗口中选择的是服务器端报表,“打印”按钮就出现了(如图1所示)。


图1 “打印”按钮的出现与隐藏

   这个问题涉及到服务器端报表(对应RDL)和客户端报表(对应RDLC)的应用情境,对此,MSDN是这样解释的——

何时使用远程处理

基于服务器的报表功能为实现下列任务提供了方法:集中存储和管理报表、设置策略和确保对报表及文件夹的安全访问、控制处理和分布报表的方式,以及设置在业务中使用报表的标准方式。Reporting Services 可以以单服务器、分布式和群集配置的方式进行安装。如果报表具有下列特征,请考虑使用远程处理:

  • 报表将被许多用户访问。
  • 报表有一个非常复杂的查询或包含大量数据,从而导致应用程序所在的计算机上的系统资源超载。
  • 报表已发布在报表服务器上,并且希望将其包含在所创建和部署的应用程序中。

——将 ReportViewer 配置为进行远程处理



何时使用本地处理

建议对于包括中小型号报表和数据集的应用程序使用本地处理模式。由于所有数据和报表的处理都是在客户端进行的,因此,如果您试图处理大型或复杂的报表和查询,性能可能会降低。如果您需要简单的部署策略,其中应用程序的所有部分都在同一台计算机上一起运行,也建议使用本地处理模式。本地处理模式的功能不及远程处理强大,它适用于不需要报表服务器的独立应用程序。熟悉在远程 SQL Server Reporting Services 报表服务器上运行的服务器报表的用户应注意以下特别之处:

  • 客户端报表定义 (.rdlc) 中的报表参数不映射到查询参数。客户端报表定义中没有参数输入区域,它接受随后在查询中使用的值。
  • 客户端报表定义不包含嵌入式查询信息。您必须定义返回可供报表使用的数据的数据源。
  • 通过 RSClientPrint ActiveX 控件执行的基于浏览器的打印不适用于 ReportViewer Web 服务器控件中运行的客户端报表定义。打印控件是报表服务器功能集的一部分。

——将 ReportViewer 配置为进行本地处理

    回到本随笔开头的问题,上面红色黑体的部分已经说明白了,Web窗体中的ReportViewer控件使用LocalReport是不能使用打印按钮进行打印的。

   在没有看到上面引用的两段来自MSDN的描述时,我还进行了一些尝试,结果当然是不成功的,但是还是记录下来这个过程:

   为了对比在Web项目中使用LocalReport和ServerReport的不同,我在Web项目中新建了两个页面wfLocalReport.aspx和wfServerReport.aspx,在这两个页面上分别放置一个ReportViewer控件,并在“ReportViewer 任务”中分别指定一个客户端报表和一个服务器端报表。我们已经知道,无论在设计时还是在运行时,前者是没有打印按钮的,而后者是有的。前者的打印按钮是不是隐藏了?如果是,可以通过某种方法把它显示出来吗?

   对比一下ReportViewer控件的属性,发现除了LocalReport和ServerReport两个属性设置不同之外并无其它不同之处,而且两者的ShowPrintButton属性均已默认设置为True。

   运行这两个页面,在浏览器中右键“查看源文件”,这时候我们会发现,返回到客户端的HTML确实是不同的,前者根本就没有表示打印按钮的HTML代码出现,而后者就有——

ContractedBlock.gif ExpandedBlockStart.gif 代码1:wfServerReport.aspx向客户端发送的HTML文件中的“打印”按钮
None.gif<input type="image" name="ReportViewer1$ctl01$ctl07$ctl00$ctl00$ctl00" src="/LRPrintInWA/Reserved.ReportViewerWebControl.axd?OpType=Resource&amp;Version=8.0.50727.42&amp;Name=Icons.Print.gif" onclick="ClientToolbarReportViewer1_ctl01.LoadPrintControl();return false;" style="height:16px;width:16px;border-width:0px;padding:2px;" />

   在上面的代码中,LoadPrintControl()方法从何而来呢?使用路径http://localhost:2167/LRPrintInWA/Reserved.ReportViewerWebControl.axd?OpType=Resource&Version=8.0.50727.42&Name=Scripts.ReportViewer.js可以得到一个名为Scripts.ReportViewer.js的.js文件,LoadPrintControl()方法就定义在该文件中——

ContractedBlock.gif ExpandedBlockStart.gif 代码2:LoadPrintControl方法
None.giffunction LoadPrintControl()
ExpandedBlockStart.gifContractedBlock.gif
dot.gif{
InBlock.gif    
var printFrame = GetControl(this.m_printFrame);
InBlock.gif    
if (printFrame != null)
ExpandedSubBlockStart.gifContractedSubBlock.gif    
dot.gif{
InBlock.gif        
if (printFrame.src != this.m_printHtmlLink)
ExpandedSubBlockStart.gifContractedSubBlock.gif        
dot.gif{
InBlock.gif            window.frames[
this.m_printFrame].window.location.replace(this.m_printHtmlLink);
ExpandedSubBlockEnd.gif        }

InBlock.gif        
else
InBlock.gif            eval(
this.m_printFrame + ".Print();");
ExpandedSubBlockEnd.gif    }

InBlock.gif    
InBlock.gif    
return false;
ExpandedBlockEnd.gif}

None.gifRSToolbar.prototype.LoadPrintControl 
= LoadPrintControl;

   另外,我们在该.js文件中还可以发现一个名为RSToolbar的函数——

ContractedBlock.gif ExpandedBlockStart.gif 代码3:RSToolbar构造函数
None.gif// Toolbar class constructor
None.gif
function RSToolbar(clientController, currentPageID, totalPagesID, firstEnableMethod, 
None.gif
None.gifpreviousEnableMethod,
None.gif    nextEnableMethod, lastEnableMethod, zoomID, findTextID, findController, findNextController, 
None.gif
None.gifformatsID, exportController, parametersRowID,
None.gif    docMapController, docMapGroupID, docMapVisibilityStateID, printHtmlLink, printFrame, 
None.gif
None.gifexportUrlBase, enablePrintMethod)
ExpandedBlockStart.gifContractedBlock.gif
dot.gif{
InBlock.gif    
this.m_clientController = clientController;
InBlock.gif    
this.m_currentPageID = currentPageID;
InBlock.gif    
this.m_totalPagesID = totalPagesID;
InBlock.gif    
this.m_zoomID = zoomID;
InBlock.gif    
this.m_findTextID = findTextID;
InBlock.gif    
this.m_findController = findController;
InBlock.gif    
this.m_findNextController = findNextController;
InBlock.gif    
this.m_formatsID = formatsID;
InBlock.gif    
this.m_exportController = exportController;
InBlock.gif    
this.m_parametersRowID = parametersRowID;
InBlock.gif    
this.m_docMapController = docMapController;
InBlock.gif    
this.m_docMapGroupID = docMapGroupID;
InBlock.gif    
this.m_docMapVisibilityStateID = docMapVisibilityStateID;
InBlock.gif    
this.m_printHtmlLink = printHtmlLink;
InBlock.gif    
this.m_printFrame = printFrame;
InBlock.gif    
this.m_exportUrlBase = exportUrlBase;
InBlock.gif    
this.m_enablePrintMethod = enablePrintMethod;
InBlock.gif    
InBlock.gif    
// Hook up methods
InBlock.gif
    this.EnableFirstNav = firstEnableMethod;
InBlock.gif    
this.EnablePreviousNav = previousEnableMethod;
InBlock.gif    
this.EnableNextNav = nextEnableMethod;
InBlock.gif    
this.EnableLastNav = lastEnableMethod;
InBlock.gif    
InBlock.gif    
this.m_nextHit = 0;
InBlock.gif    
InBlock.gif    
this.m_clientController.SetToolBar(this);
ExpandedBlockEnd.gif}

   这显然是ReportViewer的工具栏的构造函数,而我们对比wfLocalReport.aspx和wfServerReport.aspx两个页面返回的HTML文件,可以发现,它们的构造是不同的——

ContractedBlock.gif ExpandedBlockStart.gif 代码4:wfLocalReport.aspx向客户端发送的HTML文件中ReportViewer控件的工具栏的构造
None.gifvar ClientToolbarReportViewer1_ctl01 = new RSToolbar(ClientControllerReportViewer1, "ReportViewer1_ctl01_ctl01_ctl02""ReportViewer1_ctl01_ctl01_ctl04", ClientImageToggleReportViewer1_ctl01_ctl01_ctl00, ClientImageToggleReportViewer1_ctl01_ctl01_ctl01, ClientImageToggleReportViewer1_ctl01_ctl01_ctl05, ClientImageToggleReportViewer1_ctl01_ctl01_ctl06, "ReportViewer1_ctl01_ctl03_ctl00""ReportViewer1_ctl01_ctl04_ctl00", TextLinkReportViewer1_ctl01_ctl04_ctl01, TextLinkReportViewer1_ctl01_ctl04_ctl03, "ReportViewer1_ctl01_ctl05_ctl00", TextLinkReportViewer1_ctl01_ctl05_ctl01, "ReportViewer1_ctl00", HoverImageReportViewer1_ctl01_ctl00_ctl00, "ReportViewer1_ctl01_ctl00""ReportViewer1_ctl01_ctl00_ctl01""""PrintFrameReportViewer1_ctl01_ctl07""/LRPrintInWA/Reserved.ReportViewerWebControl.axd?Mode=true&ReportID=6e0a5ce971814c529735c9c1bb3c1edf&ControlID=cfa12ca1-06c0-4d45-9c83-a41706b9b141&Culture=2052&UICulture=2052&ReportStack=1&OpType=Export&FileName=Report&ContentDisposition=OnlyHtmlInline&Format=", ClientImageToggleReportViewer1_ctl01_ctl07_ctl00);

ContractedBlock.gif ExpandedBlockStart.gif 代码5:wfServerReport.aspx向客户端发送的HTML文件中ReportViewer控件的工具栏的构造
None.gifvar ClientToolbarReportViewer1_ctl01 = new RSToolbar(ClientControllerReportViewer1, "ReportViewer1_ctl01_ctl01_ctl02""ReportViewer1_ctl01_ctl01_ctl04", ClientImageToggleReportViewer1_ctl01_ctl01_ctl00, ClientImageToggleReportViewer1_ctl01_ctl01_ctl01, ClientImageToggleReportViewer1_ctl01_ctl01_ctl05, ClientImageToggleReportViewer1_ctl01_ctl01_ctl06, "ReportViewer1_ctl01_ctl03_ctl00""ReportViewer1_ctl01_ctl04_ctl00", TextLinkReportViewer1_ctl01_ctl04_ctl01, TextLinkReportViewer1_ctl01_ctl04_ctl03, "ReportViewer1_ctl01_ctl05_ctl00", TextLinkReportViewer1_ctl01_ctl05_ctl01, "ReportViewer1_ctl00", HoverImageReportViewer1_ctl01_ctl00_ctl00, "ReportViewer1_ctl01_ctl00""ReportViewer1_ctl01_ctl00_ctl01""/LRPrintInWA/Reserved.ReportViewerWebControl.axd?ReportSession=zrve2z55iaj3ttrfkd2owrfq&ControlID=6f50f184-8cd0-4aa2-9caa-3000112b1e09&Culture=2052&UICulture=2052&ReportStack=1&OpType=PrintHtml""PrintFrameReportViewer1_ctl01_ctl07""/LRPrintInWA/Reserved.ReportViewerWebControl.axd?ReportSession=zrve2z55iaj3ttrfkd2owrfq&ControlID=6f50f184-8cd0-4aa2-9caa-3000112b1e09&Culture=2052&UICulture=2052&ReportStack=1&OpType=Export&FileName=Report1&ContentDisposition=OnlyHtmlInline&Format=", ClientImageToggleReportViewer1_ctl01_ctl07_ctl00);

   仔细对比代码4和代码5,可以发现代码4中倒数第四个参数为空字符串,那么,我们有理由怀疑代码5中的倒数第四个参数,运行Web项目进入页面wfServerReport.aspx,查看源文件,取出这个参数的字符串值将其附加到地址栏中(类似这样的一个URL:http://localhost:2167/LRPrintInWA/Reserved.ReportViewerWebControl.axd?ReportSession=5z3m52ucuk4avi55xh0yx0jr&ControlID=6bea4929-cf7e-4ed6-bb88-360e33f26d41&Culture=2052&UICulture=2052&ReportStack=1&OpType=PrintHtml),回车,哈哈,我们看到了什么?


图2 通过一个URL调出的打印窗口(点击小图看大图)

   对照LoadPrintControl方法来看,之所以页面中会生成iframe,是为了保持页面的状态,而只需更改该iframe的location就可以调出该打印窗口。同样的,报表的其它操作也是通过这种方法来实现的。

   那么,我们就此是否就可以有很多想法了?比如,在wfLocalReport.aspx中自己手动增加一个按钮也按照上面的方法进行报表的打印?

   这就不好说了,说不定一不留神,这个问题解决了,呵呵,那样的话,基本上就可以用RDLC代替Reporting Services了,我们似乎也就不用买SSRS的license了,嘿嘿,妄想中……

   现在,我们需要分析一下这个URL——http://localhost:2167/LRPrintInWA/Reserved.ReportViewerWebControl.axd?ReportSession=5z3m52ucuk4avi55xh0yx0jr&ControlID=6bea4929-cf7e-4ed6-bb88-360e33f26d41&Culture=2052&UICulture=2052&ReportStack=1&OpType=PrintHtml。

   Reserved.ReportViewerWebControl.axd是什么?恩,这是个问题,并不存在这个物理文件,MSDN上也没有说明,但是在Web.Config中我们可以看到以下代码——

ContractedBlock.gif ExpandedBlockStart.gif 代码6:Web.Config中的Reserved.ReportViewerWebControl.axd
None.gif<httpHandlers>
None.gif    
<add path="Reserved.ReportViewerWebControl.axd" verb="*" type="Microsoft.Reporting.WebForms.HttpHandler, Microsoft.ReportViewer.WebForms, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" validate="false"/>
None.gif
</httpHandlers>

   也就是说,其实我们并不需要关心Reserved.ReportViewerWebControl.axd是什么,遇到客户端请求这个文件,没关系,ASP .NET引擎会搞定它。

   接下来看上述URL的参数。如果我们能够知道各个参数的来源以及在程序中取得参数值的方法,那么我们也可以使用类似上面的一个URL来进行报表的打印了。恩,我们的目标是知道在使用RDLC报表的情境下这些参数的值,但是在此之前,我们需要在ServerReport情境下找到这些参数的来源,然后对应看在RDLC报表情境下是否可以取得这些参数的值,如果对应的结果是肯定的,那么,我们也就可以实现客户端报表的打印了。

   首先,参数Culture、UICulture、ReportStack和OpType的取值应该是确定的,虽然可能并不是非常清楚这些参数的含义,但不妨猜测一下:Culture和UICulture不用说了吧,从其取值2052(简体中文)就可以知道大差不离了;ReportStack,估计是和堆栈是有关的,看其取值,估计1应该是一种方式或属性等的代码;而OpType应该是操作类型,取值PrintHTML就是我们要做的事情嘛。

   而对于参数ControlID来说,是ReportViewer控件的标识符,好像Microsoft并没有公开获取该值的方法(也许是我没找到。:)),不过ReportViewer控件的非公有成员m_instanceIdentifier的取值就是这个ControlID(如图3所示)。


图3 ReportViewer控件的非公有成员m_instanceIdentifier

   不过,这个ControlID我们是不用发愁的,我们在Web窗体发送到客户端的HTML文件中是可以找到的,缩放等报表操作已经提供了这个值,取过来就OK了。

   最后就剩一个ReportSession了,ReportSession是什么呢?怎样得到ReportSession的值呢?使用和参数ControlID一样的方法从客户端HTML文件中取?

   事实上,参数ControlID在wbLocalReport.aspx和wbServerReport.aspx这两个Web窗体发送到客户端的HTML文件中都出现了多次,参数ReportSession在wbServerReport.aspx发送到客户端的HTML文件中也出现了多次,而在wbLocalReport.aspx发送到客户端的HTML文件中并没有出现。

   既然参数的名称是ReportSession,那么会不会和Session有关呢?在页面中监视一下this.Session,发现this.Session[0]是一个Microsoft.Reporting.WebForms.ReportInfo结构,也可以在改结构中发现一个非公有成员m_executionID(如图4所示),其值和参数ReportSession的取值相同。


图4 ReportViewer控件的非公有成员m_executionID

   而且,Microsoft也实现了一个方法用于获取该值:this.ReportViewer1.ServerReport.GetExecutionId()。

   上面描述的两个参数是ServerReport才具备的特征,那么LocalReport呢?参数ControlID是OK的,而参数ReportSession在LocalReport中是无法取到的,这是非常遗憾的地方。而上述URL的ReportSession参数是无法省略或随便赋值的(会出现“ASP.NET 会话已过期”错误),这也就是说,我们的努力已经宣告落空了…………

   本来和一个朋友说昨天晚上就发这篇随笔的,到现在才发出来,抱歉!另外,这篇随笔其实并没有意义,只是对在Web项目中使用RDLC报表时如何打印的尝试,而且是一个失败的尝试。希望后来的朋友不再使用RDLC在Web项目中做报表,除非不需要使用打印功能,在这个意义上,本随笔可以算作是对这个结论的一个证明。

转载于:https://www.cnblogs.com/oscsoft/archive/2007/07/08/810231.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值