How can I use a reportviewer control in an asp.net mvc 3 razor view?
The following solution works only for single page reports. Refer to comments for more details.
ReportViewer is a server control and thus can not be used within a razor view. However you can add a ASPX view page, view user control or traditional web form that containing a ReportViewer into the application.
You will need to ensure that you have added the relevant handler into your web.config.
If you use a ASPX view page or view user control you will need to set AsyncRendering to false to get the report to display properly.
Update:
Added more sample code. Note there are no meaningful changes required in Global.asax.
Web.Config
Mine ended up as follows:
<?xml version="1.0"?>
<!--
For more information on how to configure your ASP.NET application, please visit
http://go.microsoft.com/fwlink/?LinkId=152368
-->
<configuration>
<appSettings>
<add key="webpages:Version" value="1.0.0.0"/>
<add key="ClientValidationEnabled" value="true"/>
<add key="UnobtrusiveJavaScriptEnabled" value="true"/>
</appSettings>
<system.web>
<compilation debug="true" targetFramework="4.0">
<assemblies>
<add assembly="System.Web.Abstractions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
<add assembly="System.Web.Helpers, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
<add assembly="System.Web.Routing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
<add assembly="System.Web.Mvc, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
<add assembly="System.Web.WebPages, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
<add assembly="Microsoft.ReportViewer.WebForms, Version=10.0.0.0, Culture=neutral, PublicKeyToken=B03F5F7F11D50A3A"/>
<add assembly="Microsoft.ReportViewer.Common, Version=10.0.0.0, Culture=neutral, PublicKeyToken=B03F5F7F11D50A3A"/>
</assemblies>
</compilation>
<authentication mode="Forms">
<forms loginUrl="~/Account/LogOn" timeout="2880" />
</authentication>
<pages>
<namespaces>
<add namespace="System.Web.Helpers" />
<add namespace="System.Web.Mvc" />
<add namespace="System.Web.Mvc.Ajax" />
<add namespace="System.Web.Mvc.Html" />
<add namespace="System.Web.Routing" />
<add namespace="System.Web.WebPages"/>
</namespaces>
</pages>
</system.web>
<system.webServer>
<validation validateIntegratedModeConfiguration="false"/>
<modules runAllManagedModulesForAllRequests="true"/>
<handlers>
<add name="ReportViewerWebControlHandler" preCondition="integratedMode" verb="*" path="Reserved.ReportViewerWebControl.axd" type="Microsoft.Reporting.WebForms.HttpHandler, Microsoft.ReportViewer.WebForms, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
</handlers>
</system.webServer>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Web.Mvc" publicKeyToken="31bf3856ad364e35" />
<bindingRedirect oldVersion="1.0.0.0-2.0.0.0" newVersion="3.0.0.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>
Controller
The controller actions are very simple.
As a bonus the File() action returns the output of "TestReport.rdlc" as a PDF file.
using System.Web.Mvc;
using Microsoft.Reporting.WebForms;
...
public class PDFController : Controller
{
public ActionResult Index()
{
return View();
}
public FileResult File()
{
ReportViewer rv = new Microsoft.Reporting.WebForms.ReportViewer();
rv.ProcessingMode = ProcessingMode.Local;
rv.LocalReport.ReportPath = Server.MapPath("~/Reports/TestReport.rdlc");
rv.LocalReport.Refresh();
byte[] streamBytes = null;
string mimeType = "";
string encoding = "";
string filenameExtension = "";
string[] streamids = null;
Warning[] warnings = null;
streamBytes = rv.LocalReport.Render("PDF", null, out mimeType, out encoding, out filenameExtension, out streamids, out warnings);
return File(streamBytes, mimeType, "TestReport.pdf");
}
public ActionResult ASPXView()
{
return View();
}
public ActionResult ASPXUserControl()
{
return View();
}
}
ASPXView.apsx
The ASPXView is as follows.
<%@ Page Language="C#" Inherits="System.Web.Mvc.ViewPage<dynamic>" %>
<%@ Register Assembly="Microsoft.ReportViewer.WebForms, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"
Namespace="Microsoft.Reporting.WebForms" TagPrefix="rsweb" %>
<!DOCTYPE html>
<html>
<head runat="server">
<title>ASPXView</title>
</head>
<body>
<div>
<script runat="server">
private void Page_Load(object sender, System.EventArgs e)
{
ReportViewer1.LocalReport.ReportPath = Server.MapPath("~/Reports/TestReport.rdlc");
ReportViewer1.LocalReport.Refresh();
}
</script>
<form id="Form1" runat="server">
<asp:ScriptManager ID="ScriptManager1" runat="server">
</asp:ScriptManager>
<rsweb:reportviewer id="ReportViewer1" runat="server" height="500" width="500" AsyncRendering="false"></rsweb:reportviewer>
</form>
</div>
</body>
</html>
ViewUserControl1.ascx
The ASPX user control looks like:
<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl" %>
<%@ Register Assembly="Microsoft.ReportViewer.WebForms, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"
Namespace="Microsoft.Reporting.WebForms" TagPrefix="rsweb" %>
<script runat="server">
private void Page_Load(object sender, System.EventArgs e)
{
ReportViewer1.LocalReport.ReportPath = Server.MapPath("~/Reports/TestReport.rdlc");
ReportViewer1.LocalReport.Refresh();
}
</script>
<form id="Form1" runat="server">
<asp:ScriptManager ID="ScriptManager1" runat="server"></asp:ScriptManager>
<rsweb:ReportViewer ID="ReportViewer1" runat="server" AsyncRendering="false"></rsweb:ReportViewer>
</form>
ASPXUserControl.cshtml
Razor view. Requires ViewUserControl1.ascx.
@{
ViewBag.Title = "ASPXUserControl";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>ASPXUserControl</h2>
@Html.Partial("ViewUserControl1")
References
http://blogs.msdn.com/b/sajoshi/archive/2010/06/16/asp-net-mvc-handling-ssrs-reports-with-reportviewer-part-i.aspx
bind report to reportviewer in web mvc2
This is a simple task. You can follow the following steps.
- Create a folder in your solution and name it Reports.
- Add an ASP.Net web form and name it ReportView.aspx
-
Create a Class ReportData and add it to the Reports folder. Add the following code to the Class.
public class ReportData { public ReportData() { this.ReportParameters = new List<Parameter>(); this.DataParameters = new List<Parameter>(); } public bool IsLocal { get; set; } public string ReportName { get; set; } public List<Parameter> ReportParameters { get; set; } public List<Parameter> DataParameters { get; set; } } public class Parameter { public string ParameterName { get; set; } public string Value { get; set; } }
-
Add another Class and name it ReportBasePage.cs. Add the following code in this Class.
public class ReportBasePage : System.Web.UI.Page { protected ReportData ReportDataObj { get; set; } protected override void OnInit(EventArgs e) { base.OnInit(e); if (HttpContext.Current != null) if (HttpContext.Current.Session["ReportData"] != null) { ReportDataObj = HttpContext.Current.Session["ReportData"] as ReportData; return; } ReportDataObj = new ReportData(); CaptureRouteData(Page.Request); } private void CaptureRouteData(HttpRequest request) { var mode = (request.QueryString["rptmode"] + "").Trim(); ReportDataObj.IsLocal = mode == "local" ? true : false; ReportDataObj.ReportName = request.QueryString["reportname"] + ""; string dquerystr = request.QueryString["parameters"] + ""; if (!String.IsNullOrEmpty(dquerystr.Trim())) { var param1 = dquerystr.Split(','); foreach (string pm in param1) { var rp = new Parameter(); var kd = pm.Split('='); if (kd[0].Substring(0, 2) == "rp") { rp.ParameterName = kd[0].Replace("rp", ""); if (kd.Length > 1) rp.Value = kd[1]; ReportDataObj.ReportParameters.Add(rp); } else if (kd[0].Substring(0, 2) == "dp") { rp.ParameterName = kd[0].Replace("dp", ""); if (kd.Length > 1) rp.Value = kd[1]; ReportDataObj.DataParameters.Add(rp); } } } } }
-
Add ScriptManager to the ReportView.aspx page. Now add a Report Viewer to the page. In report viewer set the property AsyncRendering="false". The code is given below.
<rsweb:ReportViewer ID="ReportViewerRSFReports" runat="server" AsyncRendering="false" Width="1271px" Height="1000px" > </rsweb:ReportViewer>
-
Add two NameSpace in ReportView.aspx.cs
using Microsoft.Reporting.WebForms; using System.IO;
-
Change the System.Web.UI.Page to ReportBasePage. Just replace your code using the following.
public partial class ReportView : ReportBasePage { protected void Page_Load(object sender, EventArgs e) { if (!IsPostBack) { RenderReportModels(this.ReportDataObj); } } private void RenderReportModels(ReportData reportData) { // This is the Data Access Layer from which a method is called to fill data to the list. RASolarERPData dal = new RASolarERPData(); List<ClosingInventoryValuation> objClosingInventory = new List<ClosingInventoryValuation>(); // Reset report properties. ReportViewerRSFReports.Height = Unit.Parse("100%"); ReportViewerRSFReports.Width = Unit.Parse("100%"); ReportViewerRSFReports.CssClass = "table"; // Clear out any previous datasources. this.ReportViewerRSFReports.LocalReport.DataSources.Clear(); // Set report mode for local processing. ReportViewerRSFReports.ProcessingMode = ProcessingMode.Local; // Validate report source. var rptPath = Server.MapPath(@"./Report/" + reportData.ReportName +".rdlc"); //@"E:\RSFERP_SourceCode\RASolarERP\RASolarERP\Reports\Report\" + reportData.ReportName + ".rdlc"; //Server.MapPath(@"./Report/ClosingInventory.rdlc"); if (!File.Exists(rptPath)) return; // Set report path. this.ReportViewerRSFReports.LocalReport.ReportPath = rptPath; // Set report parameters. var rpPms = ReportViewerRSFReports.LocalReport.GetParameters(); foreach (var rpm in rpPms) { var p = reportData.ReportParameters.SingleOrDefault(o => o.ParameterName.ToLower() == rpm.Name.ToLower()); if (p != null) { ReportParameter rp = new ReportParameter(rpm.Name, p.Value); ReportViewerRSFReports.LocalReport.SetParameters(rp); } } //Set data paramater for report SP execution objClosingInventory = dal.ClosingInventoryReport(this.ReportDataObj.DataParameters[0].Value); // Load the dataSource. var dsmems = ReportViewerRSFReports.LocalReport.GetDataSourceNames(); ReportViewerRSFReports.LocalReport.DataSources.Add(new ReportDataSource(dsmems[0], objClosingInventory)); // Refresh the ReportViewer. ReportViewerRSFReports.LocalReport.Refresh(); } }
Add a Folder to the Reports Folder and name it Report. Now add a RDLC report to the Reports/Report folder and name it ClosingInventory.rdlc.
-
Now add a Controller and name it ReportController. In the controller add the following action method.
public ActionResult ReportViewer() { ViewData["reportUrl"] = "../Reports/View/local/ClosingInventory/"; return View(); }
-
Add a view page click on the ReportViewer Controller. Name the view page ReportViewer.cshtml. Add the following code to the view page.
@using (Html.BeginForm("Login")) { @Html.DropDownList("ddlYearMonthFormat", new SelectList(ViewBag.YearMonthFormat, "YearMonthValue", "YearMonthName"), new { @class = "DropDown" }) Stock In Transit: @Html.TextBox("txtStockInTransit", "", new { @class = "LogInTextBox" }) <input type="submit" onclick="return ReportValidationCheck();" name="ShowReport" value="Show Report" /> }
-
Add an Iframe. Set the property of the Iframe as follows
frameborder="0" width="1000"; height="1000"; style="overflow:hidden;" scrolling="no"
-
Add Following JavaScript to the viewer.
function ReportValidationCheck() { var url = $('#hdUrl').val(); var yearmonth = $('#ddlYearMonthFormat').val(); var stockInTransit = $('#txtStockInTransit').val() if (stockInTransit == "") { stockInTransit = 0; } if (yearmonth == "0") { alert("Please Select Month Correctly."); } else { //url = url + "dpSpYearMonth=" + yearmonth + ",rpYearMonth=" + yearmonth + ",rpStockInTransit=" + stockInTransit; url = "../Reports/ReportView.aspx?rptmode=local&reportname=ClosingInventory¶meters=dpSpYearMonth=" + yearmonth + ",rpYearMonth=" + yearmonth + ",rpStockInTransit=" + stockInTransit; var myframe = document.getElementById("ifrmReportViewer"); if (myframe !== null) { if (myframe.src) { myframe.src = url; } else if (myframe.contentWindow !== null && myframe.contentWindow.location !== null) { myframe.contentWindow.location = url; } else { myframe.setAttribute('src', url); } } } return false; }
-
Web.config file add the following key to the appSettings section
add key="UnobtrusiveJavaScriptEnabled" value="true"
-
In system.web handlers Section add the following key
add verb="*" path="Reserved.ReportViewerWebControl.axd" type = "Microsoft.Reporting.WebForms.HttpHandler, Microsoft.ReportViewer.WebForms, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"
Change your data source to your own. This solution is very simple and I think every one will enjoy it.
I am using ASP.NET MVC3 with SSRS 2008 and I couldn't get @Adrian's to work 100% for me when trying to get reports from a remote server.
Finally, I found that I needed to change the Page_Load method in ViewUserControl1.ascx to look like this:
ReportViewer1.ProcessingMode = ProcessingMode.Remote;
ServerReport serverReport = ReportViewer1.ServerReport;
serverReport.ReportServerUrl = new Uri("http://<Server Name>/reportserver");
serverReport.ReportPath = "/My Folder/MyReport";
serverReport.Refresh();
I had been missing the ProcessingMode.Remote.
References:
http://msdn.microsoft.com/en-us/library/aa337091.aspx - ReportViewer
Here is the complete solution for directly integrating a report-viewer control (as well as any asp.net server side control) in an MVC .aspx view, which will also work on a report with multiple pages (unlike Adrian Toman's answer) and with AsyncRendering set to true, (based on "Pro ASP.NET MVC Framework" by Steve Sanderson).
What one needs to do is basically:
Add a form with runat = "server"
Add the control, (for report-viewer controls it can also sometimes work even with AsyncRendering="True" but not always, so check in your specific case)
Add server side scripting by using script tags with runat = "server"
Override the Page_Init event with the code shown below, to enable the use of PostBack and Viewstate
Here is a demonstration:
<form ID="form1" runat="server">
<rsweb:ReportViewer ID="ReportViewer1" runat="server" />
</form>
<script runat="server">
protected void Page_Init(object sender, EventArgs e)
{
Context.Handler = Page;
}
//Other code needed for the report viewer here
</script>
It is of course recommended to fully utilize the MVC approach, by preparing all needed data in the controller, and then passing it to the view via the ViewModel.
This will allow reuse of the View!
However this is only said for data this is needed for every post back, or even if they are required only for initialization if it is not data intensive, and the data also has not to be dependent on the PostBack and ViewState values.
However even data intensive can sometimes be encapsulated into a lambda expression and then passed to the view to be called there.
A couple of notes though:
- By doing this the view essentially turns into a web form with all it's drawbacks, (i.e. Postbacks, and the possibility of non Asp.NET controls getting overriden)
- The hack of overriding Page_Init is undocumented, and it is subject to change at any time
There's a MvcReportViewer helper in NuGet.
http://www.nuget.org/packages/MvcReportViewer/
And this is the details:
https://github.com/ilich/MvcReportViewer
I have using this. It works great.