[download sample app]
This series is about a sample application that can take a Crystal Report, display input parameters, then display that report. In part 1 of this series, I went over the web page that shows the how to display a Crystal Report dynamically. In part 2 we went over the class that actually performed the work of determining any parameters of the Crystal Report. This final part of the series, shows how to take those two parts, and display the report.
I accomplish this by using methods on the default.aspx web page. This part of the series starts after the user has input any parameters for the report and submitted it to be viewed. The Report will be logged onto the reports datasource. Then the report will be displayed in Adobe Acrobat format.
Allowing the user to view the report
When the _defaultCR CreatePage method created the parameters, it also added a submit button.
_literalBr = Nothing
End If
Next
Dim oSubmitButton As New System.Web.UI.WebControls.Button()
oSubmitButton.Text = "View Report"
oSubmitButton.ID = "ReportButton"
AddHandler oSubmitButton.Click, AddressOf PCRMButtonClicked
' Add a control to pass the filename
Dim oLiteral2 As New System.Web.UI.LiteralControl()
oLiteral2.Text = "<input type=hidden name='FileID' value='" & Request.QueryString("FileID") & "'>"
oLiteral2.ID = "HiddenFileID"
Panel1.Controls.Add(oLiteral2)
Panel1.Controls.Add(oSubmitButton)
The button oSubmitButton adds a handler for a click event to the method PCRMButtonClicked, which in turn calls the method CreateReportToView. Now that the Parameters (if there are any) have been filled in, the user can click on the View Report button to see the report in an adobe acrobat format. I chose displaying in that format to make for easy display and client side printing.
Displaying the report
As mentioned earlier, when a user clicks on the button that says “View Report”, the method PCRMButtonClicked is called. From that method CreateReportToView, the method with the muscle is called. CreateReportToView is separated out from the actual click event code for cleaner code separation. This method iterates through the values just entered for to the corresponding parameters in the report.
Dim oReport As New CrystalDecisions.CrystalReports.Engine.ReportDocument()
Dim oPCRMCRObject As New PCRMCreateCRObjects()
Dim reportName As String = Server.MapPath("/CrystalParams") + "\customerOrders.rpt"
Dim oCRParam As CrystalDecisions.CrystalReports.Engine.ParameterFieldDefinition
Dim oDiscreteParam As New CrystalDecisions.Shared.ParameterDiscreteValue()
Dim oDefaultValues As New CrystalDecisions.Shared.ParameterValues()
We start out by instantiating a ReportDocument object. We also instatiate a PCRMCreateCRObjects class, to help us apply the appropriate parameters. The report name variable contains a hard coded report in this sample. Normally you would use a query string with some kind of report id that can be tied back to a report path and name through a database. For sample purposes, I have kept it simply hard coded.
Finally we declare the Paramater objects. First, a ParameterFieldDefinition to help in getting the name from the input form. Second, a ParameterDiscreteValue object to use to apply the value entered back to the report. Third, a ParameterValues collection object, to collect the discrete parameters to be applied to the report.
If oPCRMCRObject.IsThisAValidCrystalFile(reportName) Then
oReport.Load(reportName)
For Each oCRParam In oReport.DataDefinition.ParameterFields
' oDefaultValues = New CrystalDecisions.Shared.ParameterValues()
oDiscretePara[code language="VB.NET"]
If oPCRMCRObject.IsThisAValidCrystalFile(reportName) Then
oReport.Load(reportName)
For Each oCRParam In oReport.DataDefinition.ParameterFields
' oDefaultValues = New CrystalDecisions.Shared.ParameterValues()
oDiscreteParam.Value = GetRequest(oCRParam.ParameterFieldName & "1", oCRParam)
Once again I use the method IsThisAValidCrystalFile to verify that the report name and path passed are valid. When that test is passed, the report gets loaded into oReport. Then using the ParameterValues collection oCRParam, I iterate through oReports Parameter fields. oDiscreteParam gets the value applied using the GetRequest method.
Public Function GetRequest(ByVal _paramName As String, _
ByVal _paramField As CrystalDecisions.CrystalReports.Engine.ParameterFieldDefinition) As String
Dim _returnValue As String
Select Case _paramField.ParameterValueKind
Case CrystalDecisions.[Shared].ParameterValueKind.DateParameter
Dim _thisCal As System.Web.UI.WebControls.Calendar
_thisCal = Panel1.FindControl(_paramName)
_returnValue = CStr(_thisCal.SelectedDate)
Case CrystalDecisions.[Shared].ParameterValueKind.DateTimeParameter
Dim _thisCal As System.Web.UI.WebControls.Calendar
_thisCal = Panel1.FindControl(_paramName)
_returnValue = CStr(_thisCal.SelectedDate)
Case CrystalDecisions.[Shared].ParameterValueKind.NumberParameter
_returnValue = Request.Form(_paramName)
Case CrystalDecisions.[Shared].ParameterValueKind.StringParameter
_returnValue = Request.Form(_paramName)
End Select
Return _returnValue
End Function
GetRequest uses _paramName to help find the forms input control to use. Then, depending on what the ParamterVAlueKind is, the value is returned. If it’s a datetime parameter, then I need to instantiate a WebControls Calendar class to get the value. I utilize findcontrol to get the correct object from the form. Then I convert the selected Date to a string to pass back to the calling method.
Back to the CreateReportView method.
oDefaultValues.Add(oDiscreteParam)
oCRParam.ApplyCurrentValues(oDefaultValues)
Next
End If
oPCRMCRObject.Close()
oPCRMCRObject = Nothing
DoCRLogin(oReport)
ExportData(oReport)
Now that there is a value put into the oDiscreteParam, we can add it to the oDefault collection. Now we use the ApplyCurrntValues method of the oCRParam object from the reports ParameterFields. The iteration continues until all the Parameterfields have been touched.
After closing the custom object oPCRMCRObject, we use the DoCRLogin method to logon to the report.
Public Sub DoCRLogin(ByRef oRpt As CrystalDecisions.CrystalReports.Engine.ReportDocument)
Dim _applyLogin As New ApplyCRLogin
' use ApplyLogin object to apply login info to all tables in CR object
_applyLogin._dbName = "Northwind"
_applyLogin._passWord = "CrystalUser"
_applyLogin._serverName = "(local)"
_applyLogin._userID = "CrystalUser"
_applyLogin.ApplyInfo(oRpt)
' clean up
_applyLogin = Nothing
End Sub
DoCRLogin should be reusable among other applications. I would separate that into a new project, or perhaps into the PCRMCRObjects project. For now, it’s on the web page. Taking the reportDocument in by reference, I also instantiate a new class called ApplyCRLogin. This class has the properties for database name, userid, server name, and password. Once those are set, the ApplyInfo method is called using the referenced reportdocument.
Public Sub ApplyInfo(ByRef _oRpt As CrystalDecisions.CrystalReports.Engine.ReportDocument)
Dim oCRDb As CrystalDecisions.CrystalReports.Engine.Database = _oRpt.Database
Dim oCRTables As CrystalDecisions.CrystalReports.Engine.Tables = oCRDb.Tables
Dim oCRTable As CrystalDecisions.CrystalReports.Engine.Table
Dim oCRTableLogonInfo As CrystalDecisions.Shared.TableLogOnInfo
Dim oCRConnectionInfo As New CrystalDecisions.Shared.ConnectionInfo()
' Dim oCRBeginDateParam As CrystalDecisions.Shared.ParameterField
oCRConnectionInfo.DatabaseName = _dbName
oCRConnectionInfo.ServerName = _serverName
oCRConnectionInfo.UserID = _userID
oCRConnectionInfo.Password = _passWord
' TODO: Need to change this to allow multiple logins for multiple sources.
For Each oCRTable In oCRTables
oCRTableLogonInfo = oCRTable.LogOnInfo
oCRTableLogonInfo.ConnectionInfo = oCRConnectionInfo
oCRTable.ApplyLogOnInfo(oCRTableLogonInfo)
Next
End Sub
ApplyInfo uses those properties set to apply them to the reports different tables by iterating through the tables within the reports database. oCRTables is a collection of the reports tables. This method owes to Wolfgang Baecks method he discovered when logging on to sub reports. Currently this method applies the same login information to all the tables in the collection. If your reports mix databases or tables, you might need to modify this.
Once we are finished using Applyinfo, on to the exporting the report. Using the method ExportData, the report is output to a pdf file into the browser. This gives the user a pretty seamless experience, with the advantages of client side printing.
Public Sub ExportData(ByRef oRpt As Object)
Dim fs As IO.FileStream
Dim FileSize As Long
Dim oDest As New CrystalDecisions.Shared.DiskFileDestinationOptions
Dim ExportFileName As String = Server.MapPath("/") & ConfigurationSettings.AppSettings("ExportDir") & Session.SessionID & ".pdf"
Try
'Dim oExport As New CrystalDecisions.CrystalReports.Engine.ReportDocument.ExportOptions()
oRpt.ExportOptions.ExportDestinationType = CrystalDecisions.[Shared].ExportDestinationType.DiskFile
oRpt.ExportOptions.ExportFormatType = CrystalDecisions.[Shared].ExportFormatType.PortableDocFormat
oDest.DiskFileName = ExportFileName
oRpt.ExportOptions.DestinationOptions = oDest
oRpt.Export()
'Build Target Filename
'Send the file to the user that made the request
Response.Clear()
Response.Buffer = True
Response.AddHeader("Content-Type", "application/pdf")
' Response.AddHeader("Content-Disposition", "attachment;filename=" & Session.SessionID & ".pdf;")
fs = New IO.FileStream(ExportFileName, IO.FileMode.Open)
FileSize = fs.Length
Dim bBuffer(CInt(FileSize)) As Byte
fs.Read(bBuffer, 0, CInt(FileSize))
fs.Close()
Response.BinaryWrite(bBuffer)
Response.Flush()
Response.Close()
Catch e As Exception
End Try
End Sub
I start out setting the oRpt ExportOptions Destination to a DiskFile. Then we set ExportFormatType to the pdf type (PortableDocFormat). Next, we set the file name to the variable ExportFileName. This is created using the SessionID in the name to create a pretty random file name. This is a temporary file anyways, so for the current user, it shouldn’t matter if it overwrites another file.
Once I’ve set the Destination Options, and called the reports export method, I’m ready to write this back out to the browser.
To write it out to the browser, I clear the response object, and add a pdf header to it. Next I open the file I just created using FileStream class. The variable bBuffer is used to store the data in byte format to be written back to the browser. Once that is filled, then using Response.BinaryWrite the Response.Flush to output the pdf file to the browser.
In the End!
That’s how I dynamically create the parameters for a Crystal Report, then display that report in a pdf document using ASP.NET. This sample application is intended to allow you to create your own inexpensive Report manager. It’s a starting point not a complete solution. You might want to compare the ability to do this with using Crystal Enterprise. Enjoy, and keep the ASP.NET faith!