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! After following this guide, you'll get the following custom print elements on the Crystal Reports Viewer toolbar:

http://www.neodynamic.com/articles/H...int-Button.jpg
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!

To test this customized printing solution we have designed a sample Crystal Report (rpt) featuring a products list for MS Northwind Traders database.

Want to print a RPT report file without Preview? Refer to our walkthrough How to Directly Print a Crystal Reports to Default Client Printer from ASP.NET without Preview or Printer Dialog

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 (WCPP) - Part of WebClientPrint Solution
- Adobe Reader or Foxit Reader (recommended)

NOTE: In this guide we used VS 2010, ASP.NET 3.5 & SAP Crystal Reports for Visual Studio 2010 but the same code & concept can be applied to VS 2005/2008 and ASP.NET 2.0 as well as VS 2012 and ASP.NET 4.x with other Crystal Reports editions

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 Windows 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 Windows 98, 2000, Me, XP, Vista, Windows 7 and Windows 8 (Desk-mode). It can print to Serial Port RS232 (e.g. COM1),
Parallel Port (e.g. LPT1), your Windows-Installed Printers (USB) and Network/IP Ethernet printers.
</p>
<p>
The <strong>WCPP</strong> utility <strong>is digitally-signed with a Windows Authenticode</strong> issued by <a href="http://www.digicert.com">DigiCert, Inc.</a>. <strong>Install WCPP only if the <u>Publisher is Neodynamic SRL</u></strong>, otherwise do not proceed. <br /><br />
<a href="http://www.neodynamic.com/downloads/wcpp20-installer.exe" 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="http://fgnass.github.io/spin.js/dist/spin.min.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 == "2.0.0.0")
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.

http://www.neodynamic.com/articles/H...s-products.jpg
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. 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 = 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 = 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>

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!


Links:
This Demos
Demos
Download WebClientPrint for ASP.NET
More Information about Neodynamic WebClientPrint for ASP.NET

Neodynamic
NET Components & Controls
http://www.neodynamic.com