1

I am integrating SSRS reports in my MVC application using an ascx control called with @Html.Partial from my “ReportViewer” view.

In the ascx I have the SSRS ReportViewer control, and in the page load method I use data passed in with the model to set up the ReportViewer’s properties such as ReportPath, etc.

Reports render fine, but the interactive features such as column sorting and paging do not work. The report appears to be refreshing but the displayed data remains unchanged. Sorting and paging work fine when I execute a report in Report Manager, but not in my application.

Here is my ascx:

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<FEMTSWeb.Application.ViewModels.ReportViewerViewModel>" %>
<%@ 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_Init(object sender, System.EventArgs e)
    {
        Context.Handler = this.Page;
    }
    private void Page_Load(object sender, EventArgs e)
    {
        if (!Page.IsPostBack)
        {
            ReportViewer1.ServerReport.ReportServerUrl = new Uri(ConfigurationManager.AppSettings["ReportServerUri"]);
            ReportViewer1.ServerReport.DisplayName = Model.DisplayName;
            ReportViewer1.ServerReport.ReportPath = Model.ReportPath;
            ReportViewer1.AsyncRendering = false;
            ReportViewer1.KeepSessionAlive = false;
            ReportViewer1.ServerReport.Refresh();
        }
    }

</script>

  <form runat="server" id="frmReportViewer">
        <asp:ScriptManager ID="ScriptManager1" runat="server"></asp:ScriptManager>
        <div style="border-style:solid;">

            <rsweb:ReportViewer ID="ReportViewer1" runat="server" Visible="true" Width="100%" Height="100%" 
                AsyncRendering="false" 
                ProcessingMode="Remote" 
                SizeToReportContent="true"
                InteractivityPostBackMode="AlwaysAsynchronous"
            />
        </div>
   </form>

Here is the View from which I call the ascx:

@model FEMTSWeb.Application.ViewModels.ReportViewerViewModel

@{
    ViewBag.Title = "ReportViewer";
}

<h2>Report Viewer</h2>

@Html.Partial("_ReportViewer", model: (FEMTSWeb.Application.ViewModels.ReportViewerViewModel)ViewData["ReportViewerModel"])
Walter Levy
  • 11
  • 1
  • 5
  • I'm wondering if this is because postbacks don't work in this environment . Try the iframe approach here: http://stackoverflow.com/questions/6144513/how-can-i-use-a-reportviewer-control-in-an-asp-net-mvc-3-razor-view – Adam Tuliper Apr 28 '13 at 17:58
  • Postbacks do work. Using Firebug I discovered there is an error in the request: "Sys.WebForms.PageRequestManagerParserErrorException: The message received from the server could not be parsed." I examined the POST message but I don't see where the problem is. – Walter Levy Apr 29 '13 at 14:12
  • Interestingly, all of the report controls (paging, refresh, sort) exhibit this problem, but I can export without error. – Walter Levy Apr 29 '13 at 14:17
  • You're going to have to page manually. I can help further if you'd like I know of a synchronous solution and a pseudo-asynchronous solution (using iframe). But as for paging working in MVC it's not going to unless you do some work arounds. Sorting, if you're talking about interactive sorting, most likely isn't going to work unless you find a similar work-around. I currently am just sorting the data before sending it to the ReportViewer control. – Shelby115 Apr 29 '13 at 17:21
  • Did you try the iframe approach in the link above? – Adam Tuliper Apr 29 '13 at 19:27

2 Answers2

0

ReportViewer is a control that needs viewstate, which MVC does not have. To do Paging, you have two choices, 1. Keep your current page in the session and use ajax to keep the current page. 2. Keep a hidden iframe on the page, use jquery to refresh it, then replace the page with the iframe's contents. This gives a pseudo-asynchronous feel.

A note on #1, ReportViewer does not determine total page count until the report is rendered (after page loads), therefore your best bet is to grab it on pageload and tell the session what it is so you can better navigate your report.

**

  • Example of #1:

** ReportViewerControl.ascx - Partial View containing ReportViewer Control.

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<NMBS.Models.SelectedReport>" %>
<%@ Register Assembly="Microsoft.ReportViewer.WebForms, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" Namespace="Microsoft.Reporting.WebForms" TagPrefix="rsweb" %>
<form id="Form1" runat="server">
    <asp:ScriptManager ID="ScriptManager1" runat="server" />
    <rsweb:ReportViewer ID="ReportViewer" runat="server" Width="652px" AsyncRendering="false">
    </rsweb:ReportViewer>
</form>
<script runat="server">
    /* Prepare the ReportViewer Control to be Displayed */
    private void Page_Load(object sender, System.EventArgs e)
    {    
        // Set the PageCount Mode to Actual (Exact) instead of the Default (Approx.)
        ReportViewer.PageCountMode = PageCountMode.Actual;

        // Load Report Definition.
        // Load DataSets.
        // Etc...

        ReportViewer.CurrentPage = Convert.ToInt32(Session["CurrentPage"]);
        ReportViewer.LocalReport.Refresh();
    }
</script>

ReportView.cshtml - Razor View that will display the ReportViewer Partial.

@* ReportViewer Control *@
<div id="div-report"> 
    @* Load the Report Viewer *@
    @Html.Partial("ReportViewerControl", this.ViewData.Model) <br />
</div>

@* Scripts *@
<script type="text/javascript" src="/Scripts/jquery-1.9.1.min.js"></script>
<script type="text/javascript" src="/Scripts/Json2.js"></script>
<script type="text/javascript" src="/Scripts/ReportView-Scripts.js"></script>

ReportView-Scripts - Scripts to load ReportViewer and Navigate appropriately.

// Since the ReportViewer Control determines the PageCount at render time, we must
//  send back the Total Page Count for us to use later.
function SetTotalPages() {
    var CurrentPage = $("#Form1 > span > div > table > tbody > tr:eq(2) > td > div > div > div:eq(0) > table > tbody > tr > td:eq(4) > input").val(),
        PageCount =  $("#Form1 > span > div > table > tbody > tr:eq(2) > td > div > div > div:eq(0) > table > tbody > tr > td:eq(8) > span").html();

    // Send JSON to /Report/SetPageCount/.
    // /Report/SetPageCount/ this action sets the variable Session["PageCount"] equal to the variable passed to it.
    $.ajax({
        url:"/Report/SetPageCount/",
        type: "POST",
        dataType: "json",
        data: "{ Count:" + PageCount + "}",
        cache: false,
        contentType: 'application/json; charset=utf-8',
        success: function (response, textStatus, jqXHR) 
        { },
        error: function (jqXHR, textStatus, errorThrown) 
        { }
    });

    // When done, update the Information.
    $("#txtNavigation").val(CurrentPage.toString() + " of " + PageCount.toString());

    // Don't do unnecessary Ajax Calls.
    //  If the Report is already on the First page, don't try navigating to Previous or First.
    Nav.UpdateFunctionality();
}

var Nav = {
    // If passed true, Enables First and Previous buttons.
    // If passed false, Disables them.
    ToggleFirstPrev: function ToggleFirstPrevNav(Toggle) {
        var NavBar = $("#span-navigation");
        if (Toggle == true) {
            // Enable First and Previous.
            NavBar.children("input[title=First]").removeAttr("disabled");
            NavBar.children("input[title=Previous]").removeAttr("disabled");

        } else {
            // Disable First and Previous.
            NavBar.children("input[title=First]").attr("disabled", true);
            NavBar.children("input[title=Previous]").attr("disabled", true);
        }
    },
    ToggleLastNext: function ToggleLastNextNav(Toggle) {
        var NavBar = $("#span-navigation");
        if (Toggle == true) {
            // Enable First and Previous.
            NavBar.children("input[title=Last]").removeAttr("disabled");
            NavBar.children("input[title=Next]").removeAttr("disabled");

        } else {
            // Disable First and Previous.
            NavBar.children("input[title=Last]").attr("disabled", true);
            NavBar.children("input[title=Next]").attr("disabled", true);
        }
    },
    UpdateFunctionality: function UpdateNavBarFunctionaility() {
        var CurrentPage = $("#Form1 > span > div > table > tbody > tr:eq(2) > td > div > div > div:eq(0) > table > tbody > tr > td:eq(4) > input").val(),
            PageCount = $("#Form1 > span > div > table > tbody > tr:eq(2) > td > div > div > div:eq(0) > table > tbody > tr > td:eq(8) > span").html(),
            Navi = Nav;

        // Don't do unnecessary Ajax Calls.
        //  If the Report is already on the First page, don't try navigating to Previous or First.
        if (parseInt(CurrentPage, 10) <= 1) {
            Navi.ToggleFirstPrev(false);

        } else {
            Navi.ToggleFirstPrev(true);
        }

        // If the Report is already on the Last page, don't try navigating to Next or Last.
        if (parseInt(CurrentPage, 10) >= parseInt(PageCount, 10)) {
            Navi.ToggleLastNext(false);
        } else {
            Navi.ToggleLastNext(true);
        }
    }
};

// Makes an Ajax call telling the Action (NavReportControl) which navigation button was clicked.
//  It then on the Server-Side updates the CurrentPage Counter to the New Page (based on Nav Button Clicked).
//  On Success it Refreshes the Iframe. On Iframe Load it copies over the new ReportViewer Control.
//  (Reason we do it this way is because without the IFrame we'd have to do it synchronously to get the
//   ReportViewer Control to do it's Initialize Function).
function NavReport(e) {
    // Gets what Navigation Action the user is trying to accomplish. (Next, Previous, First, Last) (and also Apply but that'll Change)
    var Navi = { Nav: $(this).val() },
        CurrentPage = $("#Form1 > span > div > table > tbody > tr:eq(2) > td > div > div > div:eq(0) > table > tbody > tr > td:eq(4) > input").val(),
        PageCount = $("#Form1 > span > div > table > tbody > tr:eq(2) > td > div > div > div:eq(0) > table > tbody > tr > td:eq(8) > span").html();

    // Get the New ReportViewer Control.
    $.ajax({
        type: "GET",
        dataType: "json",
        data: Navi,
        cache: false,
        contentType: 'application/json; charset=utf-8',
        url: "/Report/NavReportControl", 
        success: function () {
            RefreshiFrame();
        }
    });
}

**

  • Example of #2:

**

// Refreshes the Iframe containing another instance of the ReportViewer Control.
function RefreshiFrame() {
    // Hide the Report till update is finished.
    $("#div-report").fadeOut(175);

    // Refresh the Hidden Frame on the Page.
    document.getElementById("iframe1").contentDocument.location.reload(true);
}

// This function is ran when the iFrame is finished loading.
//   SrcName - Name of the Source iFrame. Ex. "#iframe1"
//   DestName - Name of the Destination Div Ex. "#div-report"
function RefreshReport(SrcName, DestName) {
    // Copy its "div-report" and CSS then replace it with the actual pages after reload.
    var ReportViewer = $(SrcName).contents().find(DestName).html(),
        CSS = $(SrcName).contents().find("head").children("[id*='ReportViewer']").clone(),
        CurrentPage, 
        PageCount;

    // Report Current ReportViewer with new ReportViewer.
    $(DestName).html(ReportViewer);

    //Add the New CSS to your current page.
    $("head").append(CSS);

    // Make sure the Report is visible.
    $("#div-report").show();

    // Update Nav Functionality.
    // Don't do unnecessary Ajax Calls.
    //  If the Report is already on the First page, don't try navigating to Previous or First.
    Nav.UpdateFunctionality();
}
Shelby115
  • 2,816
  • 3
  • 36
  • 52
0

Use only reportViewerExample folder from below link,

https://github.com/ilich/MvcReportViewer

Install the missing references using online nuget packages. Configure your server paths,credential in web.config (Related keys are already created in web.config after viewer is installed), also appropriate changes in view Index.chtml

Its working fine with all navigation controls, without page reload.