Buy My Account


Basics
Overview
Easy Code Customization
Sample Applications
Video Demos
Customers
Free Training
Pricing & Ordering
Download Now!

Product Tour
Product Tour
Web & Cloud Applications
Mobile Applications
SharePoint Applications
Charts and Reports
Security
Team Development
What's New in V10.2

Technical Materials
Training Courses
Online Help
Technical Forums
White Papers
One Day Web Apps E-book
System Requirements
Product Roadmap
Version History

Implementing Crystal Reports in an Iron Speed Application

Michael F. Sumption
GovIS, LLC

May 17,2005
Iron Speed Designer V2.1

Introduction

There are several ways to implement Crystal Reports ("CR") in an Iron Speed Designer application. The "Code Customization Wizard" provides one example. This article expands the scope of that example by including sample code and documentation for a "Crystal Reports Viewer" page, dynamically loading and persisting reports, setting various properties, exception handling, exporting/displaying reports, and other tips and tricks.

A Tale of Two Editions

Before we get started with the code samples, let me say a brief word about two editions of CR: Visual Studio .NET 2003 Crystal Reports ("VS.NET CR") and Crystal Reports 10 Developer Edition ("CR 10 DE"). If you can afford to buy CR 10 DE or CR XI DE, do so. Why? The VS.NET CR is actually version 9.1, and the report/print engine is based on the old RDC architecture. The .NET components in CR 10 DE are based on the new RAS report/print engine, which is more extensible and handles processes more efficiently (i.e. thread based). Also, the report designer and viewers are better and have more features. For a complete list of features by version and edition, here�s the data sheet from Business Objects: http://www.businessobjects.com/global/pdf/products//crystalreports/crxi_feat_ver_ed.pdf. The code samples in this article should work in both editions, but have only been tested with CR 10 DE and Iron Speed Designer Professional Edition Version 2.1.3.

Microscope � Creating a Viewer Page

Most Iron Speed Designer applications typically have header, menu, body, and footer sections that makeup the user interface. What happens when you export/display a report as PDF? Your nice user interface ("that you spent hours creating") is replaced with the ugly Acrobat Reader toolbar and the report. So much for consistency. Well, here�s a step-by-step method to create a report viewer page that will fit right in and provide the benefits of the CR DHTML Viewer, which Acrobat Reader does not have, such as drill down and exporting to other file formats. If you still prefer to export/display directly to PDF, then see the "Goods - Exporting and Displaying" section toward the end of this article for some sample code.
  1. In your ISD application, right click the [Shared] folder and select [New Page]. In the "New Page" dialog, select the [Master Page] template and name the page "ReportViewer."

  2. Build and then close the application.
     
  3. Now we need to add the "CR Viewer" component to the page including the necessary declarations for the code-behind and references for the project. The easiest way to do this is with VS.NET. So, open VS.NET, open your project, and select [Rebuild] from the [Build] menu. You need to rebuild the project in VS.NET first before proceeding in order to resolve some class reference issues.
     
  4. Open the "ReportViewer.aspx" page in [Design] view. From the VS.NET [Toolbox - Web Forms] tab, drag and drop a "CrystalReportViewer" component onto the page at the position marked with a red rectangle below (i.e. the main content area).

    NOTE: When the "CrystalReportViewer" component is added to the page, the associated class declaration is added to the "ReportViewer.gen.aspx.vb" code-behind class and several CR namespace references are added to the project. In the project [References] node of the "Solution Explorer", the three "CrystalDecisions.Enterprise.Xxx" references can be removed.
     
  5. Click on the "CrystalReportViewer" component and go to [Properties]. Change the following properties to "False": [DisplayGroupTree], [EnableDatabaseLogonPrompt], and [EnableParameterPrompt]. The property changes will be explained in the "Monopoly - Setting Various Properties" section.
     
  6. Switch to the [HTML] view and clean up the code to look like this:

    NOTE: Be sure to remove the width and height attributes in the "CrystalReportViewer" component tag if they appear.
     
  7. Open the "ReportViewer.safe.aspx.vb" code-behind safe class and add the "CrystalReportViewer" component class declaration as follows:


    Public Class ReportViewer
        Inherits ReportViewerGen
        Public Shadows WithEvents CrystalReportViewer1 As CrystalDecisions.Web.CrystalReportViewer
     

    NOTE: This declaration allows us to write code in the safe class that references the "CrystalReportViewer" component.
     
  8. Rebuild the project, exit VS.NET, go back to ISD, and open the application.
     
  9. Open the "ReportViewer" page and switch to the [ASPX] view. Highlight and copy the following code that was previously set up in VS.NET:

    Switch to [HTML] view and paste the code at the top above the "GEN:TEMPLATE" tag.

    Switch back to the [ASPX] view and highlight and copy the following code:

    Switch to the [HTML] view and paste over (i.e. replace) the following code:

    Now you may be saying to yourself, "Wait a minute, I just did that last part in VS.NET." Well, yes you did, but ISD uses an HTML file as a template to build the aspx page whereas VS.NET does not. So, we need to copy the "CrystalReportViewer" component tag to the HTML file in order for ISD to regenerate the "ReportViewer" aspx page properly in the future.
     
  10. Almost done, there's a couple more steps to make sure that Iron Speed Designer compiles the application properly when using the Visual Basic (vbc.exe) compiler. Copy the following files to your application's "Bin" directory: "CrystalDecisions.CrystalReports.Engine.dll", "CrystalDecisions.ReportSource.dll", "CrystalDecisions.Shared.dll", "CrystalDecisions.Web.dll". In the default installation of CR 10 DE, these files are located at "C:\Program Files\Common Files\Crystal Decisions\2.5\managed."
     
  11. In your application's root directory, open the file "CompileApplication.rsp" in a text editor. Append the names of the four Crystal Decisions DLL's to the "/reference:" line as follows (comma delimited, no spaces):


    /reference:BaseClasses.dll, BaseClasses.Web.Controls.dll, System.dll,System.Data.dll, System.Drawing.dll, System.Web.dll, System.Xml.dll, CrystalDecisions.CrystalReports.Engine.dll, CrystalDecisions.ReportSource.dll, CrystalDecisions.Shared.dll, CrystalDecisions.Web.dll
     

  12. That's it. You're done with creating the viewer page, but now we need to make it work. So let's start looking through the microscope to view some tiny code.

Washing Machine - Dynamically Loading

In order for the "CR Viewer" to display a report, the [ReportSource] property must be set to an instance of a "ReportDocument" object. The "ReportDocument" object uses the [Load] method to load the report file from the file system into the object. You could hard code the name of the report file in the [Load] method. However, you could also dynamically load a report file by using a string variable that is set from the [QueryString]. Here's an example in the [Page_Load] event of the "ReportViewer.safe.aspx.vb" code-behind safe class:


Private Sub MyPage_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.Load
    Dim oRpt As New CrystalDecisions.CrystalReports.Engine.ReportDocument
    If (Not Me.IsPostBack) Then
        Dim rptFile As String = Request.QueryString.Item("ReportFile")
        oRpt.Load(Request.MapPath(Request.ApplicationPath) & "\Reports\" & rptFile)
        CrystalReportViewer1.ReportSource = oRpt
        ...
 

The path in the [Load] method specifies that the actual report file is in the "Reports" sub-directory of the virtual directory. If only our washing machine could dynamically load clothes that easy.

Monopoly � Setting Various Properties

The code above shows how the [ReportSource] property is set; however, there are a few other "ReportDocument" properties that need to be set prior such as [DatabaseLogon] or perhaps the [RecordSelectionFormula] and [Parameters]. Here's an example of how to use the database connection string key in the "web.config" file to set the [DatabaseLogon] properties of the loaded report:


Dim conStr As String
Dim userID As String
Dim pw As String
Dim serverName As String
Dim dbName As String
Dim i As Integer
Dim j As Integer

' Get db conn settings
conStr = System.Configuration.ConfigurationSettings.AppSettings.Item("Database[YourDatabaseName]1")
' Get user id
i = InStr(1, conStr, "User Id") + 8
j = InStr(i, conStr, ";")
userID = Mid(conStr, i, j - i)
' Get pw
i = InStr(1, conStr, "Password") + 9
j = conStr.Length + 1
pw = Mid(conStr, i, j - i)
' Get server name
i = InStr(1, conStr, "Data Source") + 12
j = InStr(i, conStr, ";")
serverName = Mid(conStr, i, j - i)
' Get db name
i = InStr(1, conStr, "Database") + 9
j = InStr(i, conStr, ";")
dbName = Mid(conStr, i, j - i)

' Set DB login info
oRpt.SetDatabaseLogon(userID, pw, serverName, dbName)


NOTE: Don't forget to replace [YourDatabaseName] above with your actual database name.

What if your report requires that the "Record Selection Formula" or a parameter be set dynamically at run-time by the application. Following are examples of how to set the "Record Selection Formula" and a parameter by using a [QueryString] and session variable:


Dim rsfVar As String = Request.QueryString.Item("RSFVar")
If oRpt.RecordSelectionFormula = "" Then
   oRpt.RecordSelectionFormula = "{table.field} = '" & rptVar & "'"
Else
   oRpt.RecordSelectionFormula += " and {table.field} = '" & rptVar & "'"
End If



oRpt.SetParameterValue("ParamName", CStr(Session("VarName")))


When should I use the "Record Selection Formula" instead of a parameter? When using a parameter in a report, the parameter is automatically included in the SQL statement's "WHERE" clause, and therefore a parameter value must be provided at run-time. When modifying the "Record Selection Formula," the SQL statement's "WHERE" clause is changed dynamically at run-time, which provides some flexibility. Either way, you get to pass GO and collect $200.

NOTE: If you would like the user to be prompted for the database logon or parameter values, the CR 10 DE DHTML Viewer will pop up prompt windows if you set the [EnableDatabaseLogonPrompt] or [EnableParameterPrompt] attributes to "True" in the "CR Viewer" component tag in the HTML as mentioned earlier.

PostBack Blues - Persistence

So now everything is set and you can display a report in your application, right? Well, almost -- but not quite. Since the "CR Viewer" is DHTML, as soon as you click a button on the "CR Viewer" toolbar that causes a postback, -- poof --, your report is gone. To avoid the "PostBack Blues," you need to persist the "ReportDocument" object to a good ol' session variable as such:


Dim oRpt As New CrystalDecisions.CrystalReports.Engine.ReportDocument
If (Not Me.IsPostBack) Then
    ...
    ...
   Session("ReportDoc") = oRpt
Else
   oRpt = CType(Session("ReportDoc"), CrystalDecisions.CrystalReports.Engine.ReportDocument)
   CrystalReportViewer1.ReportSource = oRpt
End If
 

BMW Z3 � Exceptional Handling

Oops! Something crashed and I got the big nasty stack dump error screen. Why didn't Iron Speed Designer's exception handling catch it? Unfortunately, the "CR Viewer" component does not expose an event to handle exceptions, so you have to roll your own by using the [Page_Error] event of the "ReportViewer" page. Also, a lot of CR's supplemental DLL's are still COM-based, which means non-managed code. The following steps will ensure that your code has exceptional handling - like your favorite sports car, according to the Business Objects KBase article http://support.businessobjects.com/library/kbase/articles/c2015775.asp.
  1. Build and close the application in ISD.
     
  2. Open VS.NET, open your project, and open the "ReportViewer.safe.aspx.vb" code-behind safe class file.
     
  3. Put the following line at the top of the page:


    Imports CrystalDecisions.CrystalReports.Engine
     

  4. In the editor's [Class Name] drop-down-listbox, select the (Page Events) option:

  5. In the editor's [Method Name] drop-down-listbox, select the [Error] option:

  6. Enter the following code in the [Page_Error] event:


    Private Sub Page_Error(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.Error

        Dim ex As Exception

        'Retrieve the last exception that occured
        ex = Server.GetLastError()
        Server.ClearError()

        ' Check if the exception is a Crystal Reports EngineException
        If TypeOf (ex) Is EngineException Then
            ' Cast the exception into an EngineException object
            Dim exEngine As EngineException = CType(ex, EngineException)

            ' Check the type of error and handle accordingly
            Select Case exEngine.ErrorID
                Case EngineExceptionErrorID.DataSourceError
                    Response.Write("An error has occurred while connecting to the database.")
                Case EngineExceptionErrorID.ExportingFailed
                    Response.Write("An error occured while exporting the report.")
                Case EngineExceptionErrorID.MissingParameterFieldCurrentValue
                    Response.Write("At least one of the parameter fields is missing a current value.")
                Case EngineExceptionErrorID.LogOnFailed
                    Response.Write("Incorrect Logon Parameters. Check your user name and password.")
                Case EngineExceptionErrorID.OutOfLicense
                    Response.Write("There are no more licenses available. Contact your network administrator.")
                Case Else
                    Response.Write("An undocumented error occurred.")
            End Select
        Else 'Display the error message
            Response.Write(ex.Message)
        End If

    End Sub


    NOTE: Of course you can be more creative with how you display the error messages.
     
  7. Build and close the application in VS.NET.

Goods � Exporting and Displaying

As I mentioned earlier in the "Microscope - Creating a Viewer Page" section, if you prefer to skip the whole "CR Viewer" page concept and go directly to PDF, here's an approach that's a little different than the ISD "Code Customization Wizard" example. This simplified approach uses the [ExportToHttpResponse] method of the "ReportDocument" object to stream the binary PDF document to the browser, which avoids the "Malicious Code" popup warning message in IE as detailed in the Business Objects KBase article: http://support.businessobjects.com/library/kbase/articles/c2016725.asp. Follow all of the steps and code from the prior sections except "Microscope - Creating a Viewer Page" and "PostBack Blues - Persistence." Remove the following code fragments from the [Page_Load] event since they are not necessary:
  • The "If (Not Me.IsPostBack) Then" structure
  • The "CrystalReportViewer1.ReportSource = oRpt" statements
Then insert this code at the end of the [Page_Load] event.


    ...
    ...
    Response.ClearContent()
    Response.ClearHeaders()
    Response.ContentType = "application/pdf"

    ' Export the document to PDF
    oRpt.ExportToHttpResponse(CrystalDecisions.Shared.ExportFormatType.PortableDocFormat, Response, False, "")
    Response.Flush()
    Response.Close()

End Sub
 

Houdini � Tips and Tricks

I'm no Houdini magician of coding, but here are a few tips and tricks that I've used over the years:

Tip #1: When you click the [Print] button on the "CR Viewer" toolbar, the "Print Options" popup window will appear. Unfortunately, the default print page range is "1 to 1" instead of "All", which drove me crazy. Also, step #1 under the "To Print" area mentions, "select the 'Open this file' option," which is now obsolete. So, here's a few steps to fix this confused popup window:
  1. Open the following file: "C:\Program Files\Common Files\Crystal Decisions\2.5\CrystalReportViewers10\js\export.js" (default location for CR 10 DE) in your favorite JavaScript editor.
     
  2. Find the following code in the getExportDialog() function:

  3. Remove the "checked" attribute from "radio2" and place it above in the "radio1" input type as such:

  4. Save and close the file.
     
  5. Open the strings_en.js (for English) file in the same directory and find the following code:


    var L_PrintStep1 = "1. In the next dialog that appears, select the \"Open this file\" option and click the OK button.";
     

    And change the text to read:


    var L_PrintStep1 = "1. Click the OK button below and the report will be displayed in Acrobat Reader.";


Tip #2: Since the "Print Options" popup window mentioned above is in fact a popup, end-users will need to disable popup blockers for this window.

Trick #1: On my reports, I like to include the User ID and date/time stamp. To accomplish this, I place the CR Special Fields - [File Author], [Print Date], and [Print Time]  in the report footer. Here's the sample code to set the [File Author] property in the "ReportViewer.safe.aspx.vb" code-behind safe class so that it displays on the report:


...
Dim uu As New UserUtils
oRpt.SummaryInfo.ReportAuthor = uu.UserID
...
CrystalReportViewer1.ReportSource = oRpt
...


"UserUtils" is a little helper class that I created to get the User ID and other properties from the Session.

Conclusion

Well, here we are at the conclusion finally, and now you have a way to integrate Crystal Reports right into your Iron Speed Designer application. Putting all of the code snippets together from this article, the [Page_Load] event of your "ReportViewer" page should look something like this:


Private Sub MyPage_Load(ByVal sender As Object, ByVal e As System.EventArgs) _
  Handles MyBase.Load
    Dim oRpt As New CrystalDecisions.CrystalReports.Engine.ReportDocument
    If (Not Me.IsPostBack) Then
        ' Additional initialization can be performed here.
        Dim rptFile As String = Request.QueryString.Item("ReportFile")
        oRpt.Load(Request.MapPath(Request.ApplicationPath) & "\Reports\" & rptFile)

        Dim conStr As String
        Dim userID As String
        Dim pw As String
        Dim serverName As String
        Dim dbName As String
        Dim i As Integer
        Dim j As Integer

        ' Get db conn settings
        conStr = System.Configuration.ConfigurationSettings.AppSettings.Item("DatabaseNorthwind1")
        ' Get user id
        i = InStr(1, conStr, "User Id") + 8
        j = InStr(i, conStr, ";")
        userID = Mid(conStr, i, j - i)
        ' Get pw
        i = InStr(1, conStr, "Password") + 9
        j = conStr.Length + 1
        pw = Mid(conStr, i, j - i)
        ' Get server name
        i = InStr(1, conStr, "Data Source") + 12
        j = InStr(i, conStr, ";")
        serverName = Mid(conStr, i, j - i)
        ' Get db name
        i = InStr(1, conStr, "Database") + 9
        j = InStr(i, conStr, ";")
        dbName = Mid(conStr, i, j - i)

        ' Set DB login info
        oRpt.SetDatabaseLogon(userID, pw, serverName, dbName)

        ' Set Record Selection Formula
        Dim rsfVar As String = Request.QueryString.Item("RSFVar")
        If oRpt.RecordSelectionFormula = "" Then
             oRpt.RecordSelectionFormula = "{Customers.CustomerName} = '" & rptVar & "'"
        Else
             oRpt.RecordSelectionFormula += " and {Customers.CustomerName} = '" & rptVar & "'"
        End If

        ' Set CR parameter
        oRpt.SetParameterValue("CustomerName", CStr(Session("CustName")))

        Dim uu As New UserUtils
        oRpt.SummaryInfo.ReportAuthor = uu.UserID

        CrystalReportViewer1.ReportSource = oRpt
        Session("ReportDoc") = oRpt
    Else
        oRpt = CType(Session("ReportDoc"), CrystalDecisions.CrystalReports.Engine.ReportDocument)
        CrystalReportViewer1.ReportSource = oRpt
    End If
End Sub
 

When you're all done, your application "ReportViewer" page may display a report like this:

As you can see in the sample screen shot above, the "CR Viewer" is fairly generic, so impress your customers with a little magical creativity of your own.

Since it took Business Objects over 3 months to ship CR XI DE to me -- too late -- I may write a brief update to this article regarding CR XI DE in the near future. In the mean time, enjoy the "CR Viewer", and feel free to post any questions in the Iron Speed Designer User Forums.

About The Author

Michael Sumption is President and CEO of GovIS, LLC, a software development and consulting firm in Northern California. Michael has been developing applications with Crystal Reports for 10+ years and has been using Iron Speed Designer for one year.
www.govis.com
 
   
 

Terms of Service Privacy Statement