Introduction
I have been working with Report Viewer and have come across a problem related to print functionality option in Modern non-IE Browsers. This article demonstrates how to work around this issue.
Abstract
Since modern browsers cannot show the print icon on RDLC ReportViewer
toolbar, we need to first add the print icon to the toolbar using JavaScript and Jquery.
This icon will be pointing to the actual server button that is responsible for print method. When the ASP button is indirectly clicked, it goes to the server and creates a PDF version of the report and returns the file name back to the page. We can then simply embed the generated PDF inside the HTML popup.
This is a summary of what we are about to show here.
Adding the Print Icon Button
First off, we need to find out if there is a print button in RDLC toolbar. If we actually find the button, then most probably, we are using Internet Explorer and accordingly there is no need to do anything because the report viewer is rendered as it should.
we can use jQuery to select that button with this selector:
var $print = $('table[title="Print"]');
If not, we should create a client-side print icon button on the fly.
How to create one that exactly looks like the original print button?
The answer is so straightforward, by copying the markup for the original button.
Here is what RDLC uses for creating the print icon:
$print = $('<div class=" " style="display:inline-block;
font-family:Verdana;font-size:8pt;vertical-align:top;"><table title="Print">
<tbody><tr><td><input name="ReportViewer1$ctl05$ctl06$ctl00$ctl00$ctl00"
title="Print" style="width: 16px; height: 16px;" type="image"
alt="Print" src="/Reserved.ReportViewerWebControl.axd?
OpType=Resource&Version=11.0.3366.16&
Name=Microsoft.Reporting.WebForms.Icons.Print.gif"></td></tr></tbody>
</table></div>');
* Notice that we created a JavaScript object which represents the markup.
This icon must be added to the RDLC viewer toolbar and this is how to find the toolbar object on the client-side:
var findtoolbar = function () {
var $all = $('table div');
for (var i = 0, len = $all.length; i < len; i++) {
if ($($all[i]).css('background-image').toLowerCase().indexOf('toolbar_bk.png') != -1)
return $($all[i]);
}
return null;
}
As you see, there is no solid identification method for the toolbar, so we used a combination of certain properties like background image along with the order of objects in the DOM.
If you happen to find a more specific means of accessing the toolbar, use it as you will.
Now we have a button but what does it do? What can we say in click event?
Basically, there should be a server-side method that can handle the print action and this method is what we are about to assign to our client button's click event.
So the next step is to create a server method or the equivalent of it as a server button.
Adding the Server-side Helper Button and Attaching it to Client Icon Button
Here, we add a server-side button to the page. It doesn't matter where it is located or what it looks like because it will be invisible to the end user.
All we should think about is how to differentiate our button from other buttons or controls on the page.
There are several ways to do this. The simplest way would be assigning a unique id to the button but this is not a very neat approach because there is no guarantee that all print buttons in our web application will have the same id and generally using id for this kind of purpose is not best practice.
The better approach would be using an specific data-attribute. You can name it anything you want but please keep in mind that the name should not be changed across the project.
What we used in here would be this attribute:
data-report-action="chrome"
The markup for the ASP.NET button would be something like this:
<asp:button data-report-action="chrome" runat="server"
ID="btnChromePrint" text="Button" OnClick="ChromePrint_Click" />
On click event, we generate a PDF version of the report, but why PDF?
Because modern browsers can show embedded PDF to the end user and simply can print the PDF.
This is a typical code for generating PDF from RDLC report object:
protected void ChromePrint_Click(object sender, EventArgs e)
{
Warning[] warnings;
string[] streamids;
string mimeType;
string encoding;
string filenameExtension;
byte[] bytes = ReportViewer1.LocalReport.Render(
"PDF", null, out mimeType, out encoding, out filenameExtension,
out streamids, out warnings);
string filename = Guid.NewGuid().ToString() + ".pdf";
string Path = Server.MapPath("~/Repository/");
using (FileStream fs = new FileStream(Path + filename, FileMode.Create))
{
fs.Write(bytes, 0, bytes.Length);
}
btnChromePrint.Attributes.Add("data-report-filename", filename);
}
What the code above does is first create a byte stream of PDF version of the report object, then actually save the physical PDF file on a path on the server and finally store the file name on another specific attribute named "data-report-filename
".
Now, we are going to attach this helper button to the client-side icon button we created before.
var r = $('input[data-report-action=chrome]');
$print = $('<div class=" " style="display:inline-block;
font-family:Verdana;font-size:8pt;vertical-align:top;">
<table title="Print"><tbody><tr><td>
<input name="ReportViewer1$ctl05$ctl06$ctl00$ctl00$ctl00"
title="Print" style="width: 16px; height: 16px;"
type="image" alt="Print" src="/Reserved.ReportViewerWebControl.axd?
OpType=Resource&Version=11.0.3366.16&
Name=Microsoft.Reporting.WebForms.Icons.Print.gif"></td></tr>
</tbody></table></div>');
$print.on('click', function () {
r.show();
r[0].click();
r.hide();
});
Now everything is wired-up and ready to work.
Preparing the Report for Print
On client page load, we check for "data-report-filename
" attribute and if it is there and has a valid value, then we go looking for PDF file and embed it inside our HTML popup modal.
So we need a page containing all the stuff for displaying the embedded PDF and some code to open this page in a popup window, passing in the report file path information.
This is pretty much all the necessary code for modal page:
<%@ Page Language="C#" AutoEventWireup="true"
CodeFile="PrintForChrome.aspx.cs" Inherits="Reports_PrintForChrome" %>
<html >
<head id="Head1" runat="server">
<title></title>
</head>
<body marginwidth="0" marginheight="0" style="background-color: rgb(38,38,38)">
<embed width="100%" height="100%" name="plugin"
src="<%=thefilename %>" type="application/pdf" >
</body>
</html>
The code behind is as follows:
public partial class Reports_PrintForChrome : System.Web.UI.Page
{
public string thefilename = "";
protected void Page_Load(object sender, EventArgs e)
{
thefilename = "/repository/" + HttpUtility.HtmlDecode(Request["filename"].ToString());
}
}
Now that we have page to display the PDF, let's get to how and when to call upon it.
There are two possible states for server button when the page loads on the client browser. It either has "data-report-filename
" or doesn't.
If it has the attribute, then we have a report to show and we need to load modal page for the file noted in attribute.
The code for opening the modal page is as follows:
if (r.attr('data-report-filename')) {
window.open('/Reports/PrintForChrome.aspx?filename=' + escape
(r.attr('data-report-filename')), "_blank", "width=650, height=400");
r.removeAttr('data-report-filename');
}
If attribute is not there, then we just build everything as stated in the above sections.
Tada! The report is ready to serve without any reference to activex objects or being forced to use Internet Explorer as the default browser.
Special Notes
This tip is intended for RDLC report version 11.0. If you want to use it on other versions, change the markup code according to what the specific version of your choice uses for making the toolbar button.