How to add Custom Printing options to ASP.NET Crystal Reports Viewer toolbar
Product WebClientPrint for ASP.NET Published 04/16/2013 Updated 11/07/2013 Author Neodynamic
Overview
Crystal Reports Viewer in ASP.NET cannot directly print to Chrome browser and this is a big problem for many developers that need to target such user browser. In this walkthrough, you'll learn how to add advanced printing capabilities to the ASP.NET Crystal Reports Viewer control that works with Internet Explorer, Chrome & Firefox as well as on Linux & Mac OS machines! After following this guide, you'll get the following custom print elements on the Crystal Reports Viewer toolbar:
ASP.NET Crystal Reports Viewer Custom Toolbar listing Installed Client Printers and Print button on Chrome
The code provided by this walkthrough allows you:
To add a custom drop-down-list of the printers installed at the client machine!
To print the Crystal Report (rpt) file to the Default Printer or any installed printer without displaying any dialog!
To provide the client-side printing feature to all main three browsers on Windows OS i.e. Internet Explorer, Firefox & Chrome as well as on Linux & Mac OS machines!
To test this customized printing solution we have designed a sample Crystal Report (rpt) featuring a products list for MS Northwind Traders database.
Requirements
Development/Server-side
WebClientPrint 2.0 for ASP.NET (or greater)
ASP.NET 3.5 (or greater)
Visual Studio 2010 / VWD 2010 (or greater)
SAP Crystal Reports for Visual Studio 2010
jQuery 1.4.1 (or greater)
Client-side
WebClientPrint Processor 2.0 for Windows, Linux & Mac (WCPP) - Part of WebClientPrint Solution
Adobe Reader or Foxit Reader (Only for Windows clients; No additional requirements for Linux & Mac systems)
Follow up these steps
- Download & install WebClientPrint for ASP.NET
- Open Visual Studio and create a new ASP.NET 3.5 Website naming it PrintRDLCReport
- Add a reference to Neodynamic.SDK.WebClientPrint.dll to your project
-
Open your web.config file and add the following entries:
<system.web> ... <httpHandlers> ... <add verb="*" path="wcp.axd" type="Neodynamic.SDK.Web.WebClientPrint, Neodynamic.SDK.WebClientPrint"/> ... </httpHandlers> ... </system.web> <system.webServer> ... <handlers> ... <add name="WCP" verb="*" path="wcp.axd" type="Neodynamic.SDK.Web.WebClientPrint, Neodynamic.SDK.WebClientPrint"/> ... </handlers> ... </system.webServer>
-
Open the default.aspx page and paste the following markup. The task of this page is to try to detect the WCPP and inform the user to install it if it's missing.
<html xmlns="http://www.w3.org/1999/xhtml"> <head runat="server"> <title></title> <style> body{font: 13px 'Segoe UI', Tahoma, Arial, Helvetica, sans-serif;} </style> <%-- WCPP-detection meta tag for IE10 --%> <%= Neodynamic.SDK.Web.WebClientPrint.GetWcppDetectionMetaTag() %> </head> <body> <form id="form1" runat="server"> <div id="msgInProgress"> <div id="mySpinner" style="width:32px;height:32px"></div> <br /> Detecting WCPP utility at client side... <br /> Please wait a few seconds... <br /> </div> <div id="msgInstallWCPP" style="display:none;"> <h3>#1 Install WebClientPrint Processor (WCPP)!</h3> <p> <strong>WCPP is a native app (without any dependencies!)</strong> that handles all print jobs generated by the <strong>WebClientPrint ASP.NET component</strong> at the server side. The WCPP is in charge of the whole printing process and can be installed on <strong>Windows, Linux & Mac!</strong> </p> <p> <a href="http://www.neodynamic.com/downloads/wcpp/" target="_blank">Download and Install WCPP from Neodynamic website</a><br /> </p> <h3>#2 After installing WCPP...</h3> <p> <a href="CrystalReportViewerCustomPrint.aspx">You can go and test WebClientPrint for ASP.NET</a> </p> </div> </form> <%-- Add Reference to jQuery at Google CDN --%> <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.8.2/jquery.min.js"></script> <%-- Add Reference to spin.js (an animated spinner) --%> <script src="//spin.js.org/spin.js"></script> <script type="text/javascript"> var wcppPingDelay_ms = 10000; function wcppDetectOnSuccess(){ <%-- WCPP utility is installed at the client side redirect to WebClientPrint sample page --%> <%-- get WCPP version --%> var wcppVer = arguments[0]; if(wcppVer.substring(0, 1) == "2") window.location.href = "CrystalReportViewerCustomPrint.aspx"; else //force to install WCPP v2.0 wcppDetectOnFailure(); } function wcppDetectOnFailure() { <%-- It seems WCPP is not installed at the client side ask the user to install it --%> $('#msgInProgress').hide(); $('#msgInstallWCPP').show(); } $(document).ready(function () { <%-- Create the Spinner with options (http://fgnass.github.io/spin.js/) --%> var spinner = new Spinner({ lines: 12, length: 7, width: 3, radius: 10, color: '#336699', speed: 1, trail: 60 }).spin($('#mySpinner')[0]); }); </script> <%-- WCPP detection script --%> <%= Neodynamic.SDK.Web.WebClientPrint.CreateWcppDetectionScript() %> </body> </html>
-
The Crystal Reports (RPT) we've created for this sample is a simple product list for MS Northwind Traders database and it has an XML data source. The report looks like the following figure.
Sample Crystal Reports (RPT) featuring Northwind Traders products list
- Download and copy both MyProducts.rpt & NorthwindProducts.xml files to your website root folder
-
Finally, add a new ASPX page and name it CrystalReportViewerCustomPrint.aspx (be sure to UNSELECT the "Place code in separate file" checkbox)
In this page, we'll customize the Crystal Reports Viewer toolbar by adding a list of installed client printers and change the Print button behavior. The user will be able to print to the Defaul Printer or any other installed printer without displaying any print dialog!
At server-side, we silently convert the report to PDF format and then we pass it to the WCPP utility to print it to the desired client printer. For Windows clients, the user is required to have Adobe Reader installed to get this code working.
Please paste the following markup and code depending on your preferred language i.e. C# or VB on your CrystalReportViewerCustomPrint.aspx file.VB
<%@ Page Language="VB" %> <%@ Register Assembly="CrystalDecisions.Web, Version=13.0.2000.0, Culture=neutral, PublicKeyToken=692fbea5521e1304" Namespace="CrystalDecisions.Web" TagPrefix="CR" %> <%@ Import Namespace="System.Data" %> <%@ Import Namespace="System.IO" %> <%@ Import Namespace="Neodynamic.SDK.Web" %> <%@ Import Namespace="CrystalDecisions.CrystalReports.Engine" %> <%@ Import Namespace="CrystalDecisions.Shared" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <script runat="server"> Protected Sub Page_Init(sender As Object, e As EventArgs) 'Print report??? If WebClientPrint.ProcessPrintJob(Request) Then 'load and set report's data source Dim ds As New DataSet() ds.ReadXml(Server.MapPath("~/NorthwindProducts.xml")) 'create and load rpt in memory Dim myCrystalReport As New ReportDocument() myCrystalReport.Load(Server.MapPath("~/MyProducts.rpt")) myCrystalReport.SetDataSource(ds.Tables(0)) 'Export rpt to a temp PDF and get binary content Dim pdfContent As Byte() = Nothing Using ms As MemoryStream = DirectCast(myCrystalReport.ExportToStream(ExportFormatType.PortableDocFormat), MemoryStream) pdfContent = ms.ToArray() End Using 'Now send this file to the client side for printing 'IMPORTANT: Adobe Reader needs to be installed at the client side 'get selected printer Dim printerName As String = Server.UrlDecode(Request("printerName")) 'create a temp file name for our PDF report... Dim fileName As String = "MyFile-" + Guid.NewGuid().ToString("N") + ".pdf" 'Create a PrintFile object with the pdf report Dim file As New PrintFile(pdfContent, fileName) 'Create a ClientPrintJob and send it back to the client! Dim cpj As New ClientPrintJob() 'set file to print... cpj.PrintFile = file 'set client printer... If printerName = "Default Printer" Then cpj.ClientPrinter = New DefaultPrinter() Else cpj.ClientPrinter = New InstalledPrinter(printerName) End If 'send it... cpj.SendToClient(Response) End If End Sub Protected Sub Page_Load(sender As Object, e As System.EventArgs) If (IsPostBack = False) Then 'load and set report's data source Dim ds As New DataSet() ds.ReadXml(Server.MapPath("~/NorthwindProducts.xml")) 'create and load rpt in memory Dim myCrystalReport As New ReportDocument() myCrystalReport.Load(Server.MapPath("~/MyProducts.rpt")) myCrystalReport.SetDataSource(ds.Tables(0)) CrystalReportViewer1.ReportSource = myCrystalReport End If End Sub </script> <html xmlns="http://www.w3.org/1999/xhtml"> <head runat="server"> <title></title> </head> <body> <form id="form1" runat="server"> <div> <CR:CrystalReportViewer ID="CrystalReportViewer1" runat="server" AutoDataBind="true" /> </div> </form> <%-- Add Reference to jQuery at Google CDN --%> <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.8.2/jquery.min.js"></script> <%-- Register the WebClientPrint script code --%> <%=Neodynamic.SDK.Web.WebClientPrint.CreateScript()%> <script type="text/javascript"> $(document).ready(function () { <%-- remove built-in print button behavior --%> $('#<%=CrystalReportViewer1.ClientID%>_toptoolbar_print').prop("onclick", null).attr("onclick", null); <%-- add our own print button behavior --%> $('#<%=CrystalReportViewer1.ClientID%>_toptoolbar_print').click(function() { jsWebClientPrint.print('printerName=' + $('#ddlClientPrinters').val()); }); $('#<%=CrystalReportViewer1.ClientID%>_toptoolbar_print').parent().parent().prepend('<select name="ddlClientPrinters" id="ddlClientPrinters" class="comboEditable" title="Select Printer"><option>Loading printers...</option></select>'); <%-- load client printers through WebClientPrint --%> jsWebClientPrint.getPrinters(); }); <%-- Time delay we'll wait for getting client printer names --%> var wcppGetPrintersDelay_ms = 5000; //5 sec function wcpGetPrintersOnSuccess(){ <%-- Display client installed printers --%> if(arguments[0].length > 0){ var p=arguments[0].split("|"); var options = '<option>Default Printer</option>'; for (var i = 0; i < p.length; i++) { options += '<option>' + p[i] + '</option>'; } $('#ddlClientPrinters').html(options); }else{ alert("No printers are installed in your system."); } } function wcpGetPrintersOnFailure() { <%-- Do something if printers cannot be got from the client --%> alert("No printers are installed in your system."); } </script> </body> </html>
C#
<%@ Page Language="C#" %> <%@ Register Assembly="CrystalDecisions.Web, Version=13.0.2000.0, Culture=neutral, PublicKeyToken=692fbea5521e1304" Namespace="CrystalDecisions.Web" TagPrefix="CR" %> <%@ Import Namespace="System.Data" %> <%@ Import Namespace="System.IO" %> <%@ Import Namespace="Neodynamic.SDK.Web" %> <%@ Import Namespace="CrystalDecisions.CrystalReports.Engine" %> <%@ Import Namespace="CrystalDecisions.Shared" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <script runat="server"> protected void Page_Init(object sender, EventArgs e) { //Print report??? if (WebClientPrint.ProcessPrintJob(Request)) { //load and set report's data source DataSet ds = new DataSet(); ds.ReadXml(Server.MapPath("~/NorthwindProducts.xml")); //create and load rpt in memory ReportDocument myCrystalReport = new ReportDocument(); myCrystalReport.Load(Server.MapPath("~/MyProducts.rpt")); myCrystalReport.SetDataSource(ds.Tables[0]); //Export rpt to a temp PDF and get binary content byte[] pdfContent = null; using (MemoryStream ms = (MemoryStream)myCrystalReport.ExportToStream(ExportFormatType.PortableDocFormat)) { pdfContent = ms.ToArray(); } //Now send this file to the client side for printing //IMPORTANT: Adobe Reader needs to be installed at the client side //get selected printer string printerName = Server.UrlDecode(Request["printerName"]); //create a temp file name for our PDF report... string fileName = "MyFile-" + Guid.NewGuid().ToString("N") + ".pdf"; //Create a PrintFile object with the pdf report PrintFile file = new PrintFile(pdfContent, fileName); //Create a ClientPrintJob and send it back to the client! ClientPrintJob cpj = new ClientPrintJob(); //set file to print... cpj.PrintFile = file; //set client printer... if (printerName == "Default Printer") cpj.ClientPrinter = new DefaultPrinter(); else cpj.ClientPrinter = new InstalledPrinter(printerName); //send it... cpj.SendToClient(Response); } } protected void Page_Load(object sender, EventArgs e) { if (IsPostBack == false) { //load and set report's data source DataSet ds = new DataSet(); ds.ReadXml(Server.MapPath("~/NorthwindProducts.xml")); //create and load rpt in memory ReportDocument myCrystalReport = new ReportDocument(); myCrystalReport.Load(Server.MapPath("~/MyProducts.rpt")); myCrystalReport.SetDataSource(ds.Tables[0]); CrystalReportViewer1.ReportSource = myCrystalReport; } } </script> <html xmlns="http://www.w3.org/1999/xhtml"> <head runat="server"> <title></title> </head> <body> <form id="form1" runat="server"> <div> <CR:CrystalReportViewer ID="CrystalReportViewer1" runat="server" AutoDataBind="true" /> </div> </form> <%-- Add Reference to jQuery at Google CDN --%> <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.8.2/jquery.min.js"></script> <%-- Register the WebClientPrint script code --%> <%=Neodynamic.SDK.Web.WebClientPrint.CreateScript()%> <script type="text/javascript"> $(document).ready(function () { <%-- remove built-in print button behavior --%> $('#<%=CrystalReportViewer1.ClientID%>_toptoolbar_print').prop("onclick", null).attr("onclick", null); <%-- add our own print button behavior --%> $('#<%=CrystalReportViewer1.ClientID%>_toptoolbar_print').click(function() { jsWebClientPrint.print('printerName=' + $('#ddlClientPrinters').val()); }); $('#<%=CrystalReportViewer1.ClientID%>_toptoolbar_print').parent().parent().prepend('<select name="ddlClientPrinters" id="ddlClientPrinters" class="comboEditable" title="Select Printer"><option>Loading printers...</option></select>'); <%-- load client printers through WebClientPrint --%> jsWebClientPrint.getPrinters(); }); <%-- Time delay we'll wait for getting client printer names --%> var wcppGetPrintersDelay_ms = 5000; //5 sec function wcpGetPrintersOnSuccess(){ <%-- Display client installed printers --%> if(arguments[0].length > 0){ var p=arguments[0].split("|"); var options = '<option>Default Printer</option>'; for (var i = 0; i < p.length; i++) { options += '<option>' + p[i] + '</option>'; } $('#ddlClientPrinters').html(options); }else{ alert("No printers are installed in your system."); } } function wcpGetPrintersOnFailure() { <%-- Do something if printers cannot be got from the client --%> alert("No printers are installed in your system."); } </script> </body> </html>
-
That's it! Run your website and test it. WebClientPrint will load the installed printers at the client machine and will list it in the CrystalReportViewer toolbar. User can select any of the installed printers and by clicking on Print icon button the printing process will be performed from IE, Firefox or Chrome as well as from Linux & Mac OS clients!
On Windows clients, Adobe Reader stays open after printing
That's a known behavior of Adobe Reader 8.0 or greater. There's no way to close it from our part, sadly. The only suggestion is to install Foxit Reader (a free PDF Viewer) instead. If Foxit Reader is installed, the printing is performed without displaying it to the end user!