As you probably already know, there’s no built-in way to specify the filename of a report you want to export to PDF, Excel, Word, etc. Then a colleague pointed me to this article.
First off, the code for the below solution can be downloaded here.
This one took a while to figure out so I hope this saves people a few hours of tinkering. As you probably already know, there’s no built-in way to specify the filename of a report you want to export to PDF, Excel, Word, etc. At first I thought of attempting to modify the viewer.aspx
file by inserting Javascript there. It probably is possible to do it that route, but many people would be averse to attempting to do such a thing. Then a colleague pointed me to this article, which uses Javascript to rename the file using SSRS’s URL parameter FileName
.
We decided to try out the code, so we added it as a web resource and placed the setHandler
function in the onload
event of the Quote form. Testing it out, it worked without a hitch, however, I wasn’t too fond of using setTimeout
in Javascript; from past experience, using a paradigm that depends on timing is a mistake. So, I went about rewriting the code to utilize an event-driven approach (onload
, onreadystatechange
, etc.). After several hours, I finally understood why Alex (who wrote the above article) used setTimeout
in his code:
- When
viewer.aspx
loads, it first loads a blank HTML file (/_static/blank.htm
) into aniframe
. - Javascript is then used to dynamically build the HTML within the
iframe
using an object calledreportViewer
. - When you export a report, a URL is dynamically generated based on the
ExportUrlBase
property of thereportViewer
object.
The key here is to modify the ExportUrlBase
property because that is what contains the FileName
URL parameter. You can inspect ExportUrlBase
in the report window using IE8’s Developer Tools (hit F12):
document.getElementById("resultFrame").contentWindow.$find("reportViewer")._getInternalViewer().ExportUrlBase
Which should result to something similar the following:
/Reserved.ReportViewerWebControl.axd?ReportSession=<SOME_SESSION_ID>&Culture=1033&CultureOverrides=True&UICulture=1033&UICultureOverrides=True&ReportStack=1&ControlID=<SOME_CONTROL_ID>&OpType=Export&FileName=<REPORT_FILE_NAME>&ContentDisposition=OnlyHtmlInline&Format=
Given the above, attempting to set the ExportUrlBase
when the iframe
finishes loading or when the DOM is ready won’t work (specifically because everything is generated via Javascript); in fact, the onload
for the iframe
will fire, but remember that the source for the iframe is a blank document! Unfortunately, the best way to tackle this is to use timing to ensure that the reportViewer
object finally gets loaded. So, in the end, I did end up using a timing function but in this case, I used setInterval
instead of a recursive setTimeout
; in addition, I also utilized regex
to change the FileName
value, both of which resulted in a simpler approach.
To use the script below, create a new Web Resource in CRM and set the onload
event for the form to setReportFilename
. For convenience, you can download the script here.
function setReportFilename() { var newFileName = Xrm.Page.getAttribute("quotenumber").getValue(); // Change to anything you want window.open = function (open) { return function (url, name, features) { var newWindow = open(url, name, features); // Set to variable so we can run additional Javascript if(url.indexOf("viewer.aspx") > 0) { // Make sure it's the viewer.aspx file var inter = window.setInterval(function() { // Start the timer to keep looking for the reportViewer object try { var fr = newWindow.document.getElementById("resultFrame").contentWindow; var rv = fr.$find("reportViewer"); var r = rv._getInternalViewer(); r.ExportUrlBase = r.ExportUrlBase.replace(/&FileName=.+?&/, "&FileName=" + newFileName + "&") window.clearInterval(inter); // Found it, stop the timer } catch (err) { return; } }, 100); }; return newWindow; }; }(window.open); // Override the window.open function without polluting global namespace };