Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

CrystalReportViewer Buttons Broken using MVC Framework

We are using the MVC framework (release 5) and the CrystalReportViewer control to show our reports. I cannot get any of the buttons at the top of the report viewer control to work.

If I'm working with the report 'HoursSummary'. If I hover over any of the buttons on the report viewer in IE the displayed link at the bottom of the pages is '../HoursSummary'. This creates a url of 'http://localhost/HoursSummary'. There is no 'HoursSummary' controller so I keep receiving 404 errors.

  • I believe I want to redirect to 'http://localhost/reports/HoursSummary' since I do have a reports controller. If this is the correct method does anyone know which property I should set on the CrystalReportViewer control to make that happen?
  • Is there an easier method to handle this situation?
like image 821
ben Avatar asked Sep 30 '08 19:09

ben


2 Answers

If that's a server control, it won't work. ASP.NET MVC doesn't use any postbacks, so most of webforms server controls don't function.

What you can do is embed the report viewer in an iFrame and output that in your MVC view. The iframe can point to a page outside of the MVC stuff, say in a subfolder called Legacy or something.

like image 164
Ben Scheirman Avatar answered Sep 30 '22 11:09

Ben Scheirman


We were able to get the report viewer to work and have been using it for the past few months in production without any issues.

  • We have a reports controller that lists links to the reports we want to run
  • Clicking on one of the links will make an ajax call to the back end and return a partial page where we can fill in all the parameters we need.
  • After the parameters are filled out we submit the form to '\reports\Name of Report'.
  • Back in the Reports controller we call SQL, return our data, and then call a different view called 'Full Report'
  • The 'Full Report' View only has a crystal report viewer control on it where it automatically takes the report data we pass over to it through ViewData, populates the report, renders it, and sends it to the user

Everything seems to work great.

UPDATE

I've added some code and clarification to the steps I originally listed above. The key item I left out was there is some code behind with the final View so it will work with Crystal Reports. The code behind is minimal, but needed. For Crystal Reports to work you are going to end up with the following files:

  • A layout file.rpt where you design the report
  • A aspx file that holds the Crystal Reports Report control. This is the file that will have some code behind.

Details on how to create a View that will work with Crystal Reports:

  • Create the layout of your report using the Crystal Reports Designer. The resulting file will be an .rpt file. For the sake of this example, let's call this file AllJobsSummaryReportLayout.rpt.
  • While designing your report, for the 'Database Fields' select one of the business entities or DTOs that holds the results coming back from SQL.
  • A quick aside, we have a few data transfer objects (DTOs) in our system that contain nothing more than scalar values and strings, there is no intelligence in these DTOs. When the Controller is called, it calls the Model, the Model for most of these reports returns a List of DTOs that we then pass to the View to be rendered. These DTOs do not know how to query themselves, display themselves, they only contain actual values returned from SQL that someone else then renders.
  • Once the layout Crystal Report file is completed, the AllJobsSummaryReportLayout.rpt, we design our Controller. In the Controller we take in any parameters needed to run the report, call the Model, Model returns our list of DTOs, as seen in the snippet below from the Controller:

    var reportViewData = model.AllJobsSummaryQuery(startDate, endDate);
    if (0 != reportViewData.Count())
    {
        var report = new AllJobsSummaryReportLayout();
        report.SetDataSource(reportViewData);
        report.SetParameterValue("startDate", startDate);
        report.SetParameterValue("endDate", endDate);
        ViewData["ReportData"] = report;
        returnView = "AllJobsSummaryView";
    }
    else
        returnView = "noReportView";
    return View(returnView);
    
  • Note a couple of items here, we are creating a varible 'report' that is a type of the Crystal Report layout file, AllJobsSummaryReportLayout.rpt, that we created above.

  • Once we created the 'report' variable we set the data source values and any parameters we need, and bundle the item up into the ViewData.

  • Now let's take a look at AllJobsSummaryView.aspx. This file has a form on it with a Crystal Reports Viewer and a code behind file:

<%@ Page Title="All Jobs Summary Report" Language="C#" AutoEventWireup="true" CodeBehind="AllJobsSummaryView.aspx.cs" Inherits="V.Views.Reports.AllJobsSummaryView"%>     
<%@ Register Assembly="CrystalDecisions.Web, Version=10.5.3700.0, Culture=neutral, PublicKeyToken=692fbea5521e1304" Namespace="CrystalDecisions.Web" TagPrefix="CR" %> 
<form id="form1" runat="server">
<div>
<a href="/Reports" id="Report"><< Return to Report Main
    Page</a><br />
<CR:CrystalReportViewer ID="ReportViewer" runat="server" AutoDataBind="True" EnableDatabaseLogonPrompt="False"
    EnableParameterPrompt="False" HasCrystalLogo="False" DisplayGroupTree="False" 
    HasDrillUpButton="False" HasToggleGroupTreeButton="False" HasViewList="False" 
    HasSearchButton="False" EnableDrillDown="False" EnableViewState="True" 
    Height="50px" ReportSourceID="CrystalReportSource1" Width="350px" />    
<CR:CrystalReportSource ID="CrystalReportSource1" runat="server">
    <Report FileName="AllJobsSummaryReportLayout.rpt">
    </Report>
</CR:CrystalReportSource>
</div>
</form>
  • And the code behind file:

     using System;
     using System.Collections.Generic;
     using System.Linq;
     using System.Web;
     using System.Web.Mvc;
    
     namespace V.Views.Reports
     {
        public partial class AllJobsSummaryView : ViewPage
        {
            protected void Page_Init(object sender, EventArgs e)
            {
                ReportViewer.ReportSource = ViewData["ReportData"];
            }
    
            protected void Page_Unload(object sender, EventArgs e)
            {
                ((AllJobsSummaryReportLayout)ViewData["ReportData"]).Close();
                ((AllJobsSummaryReportLayout)ViewData["ReportData"]).Dispose();
            }
        }
     }
    
  • The Page_Unload is key, without it you will have an error generated by Crystal Reports 'You have exceeded the max number of reports set by your administrator.'

This method is still working in a production environment for well over two years now.

like image 28
ben Avatar answered Sep 30 '22 13:09

ben