This is an excerpt from the book Crystal Reports .Net Programming.
Click to read
Learning the Crystal Report Object Models
Part I of this book taught the details of designing reports by laying out the report objects in the different report sections and connecting to data sources. When the report was finished its design and layout stayed the same every time it was run. The data that it prints will change, but the report format is locked. With Crystal Reports .NET, reports have the flexibility to be dynamic. .NET programmers have full access to the properties of a report and the underlying report objects. These properties can be read and many of them can be written to. You get the power to create a reporting solution that takes user input and customizes each report prior to printing it. This can range from changing the formatting of report objects, modifying the grouping and sorting, and changing the data source. The more you learn about runtime customization the more you will find out what you can do. This chapter serves as the foundation for building your knowledge throughout the rest of the book.
The reason that you have so much power to modify reports is because .NET treats every report as an object-oriented class. The entire object model is exposed to your .NET program. Whether you program with VB.NET or C# isn't important. The object model can be accessed by any of the .NET languages.
There are three ways to use the Crystal Reports object model. The first is to use the ReportDocument class to reference virtually every class and property of the report. The second way is to use the methods and properties of the CrystalReportViewer control. When compared to the ReportDocument class, the viewer only has a small subset of properties and methods. The viewer lets you modify the properties that effect logging in to a data source, setting report parameters, and deciding which report to preview. The last way to work with the object model is to subscribe to the events that are triggered while the report is previewed and printed. These events are useful for knowing what part of the report is being looked at and what the user is doing. This chapter goes into detail on all three ways of working with the report classes.
If you have experience with Crystal Reports 8.5 Developer Edition, then you are probably familiar with modifying reports during runtime. One of the key features of the Developer Edition was that the Visual Studio 6.0 developer had a greater amount of control over the report during runtime. In fact, a developer could write an entire report from scratch during runtime! The report classes that come with .NET are not this sophisticated. While you do have a lot of flexibility with modifying an existing report, you are limited to using the existing report objects. You can't create new report objects nor can you change the fields that the current objects link to. If you want to create reports and add new report objects, you have to purchase a separate developer’s license. However, this license is extremely expensive and is only practical for companies with a large budget.
Basic Customization
No matter what type of runtime customization you want to perform, there are three basic steps that you need to know. Customizing reports always starts with the same premise: declare and instantiate a ReportDocument object modify its properties, and print or preview it.
Chapter 3 gave a thorough explanation of the different ways to integrate reports into an application (Strongly-Type versus Untyped). If you need a refresher, refer to the sections on binding reports for a discussion of the pros and cons of the different methods of binding reports.
Note |
The code that implements runtime customization is the same for both WinForms development and ASP.NET applications. The ReportDocument class is used with both types of applications. ASP.NET will only be mentioned for special circumstances. |
Step 1: Declare and instantiate the ReportDocument object variable.
An object variable can either instatiate the report class directly (Strongly-Typed reports) or it can instantiate the ReportDocument class and then load the report into memory (Untyped reports). Since runtime customization requires the use of Strongly-Typed reports, the examples in this chapter will use this binding method.
Note |
In the examples throughout this chapter and the rest of the book, the report class being referenced is called CrystalReport1. This is the default report name that is given by the report wizard. When you implement the sample code in your applications, replace the name CrystalReport1 with the class name of the report you want to print. All the code samples are generic and will work with every report. |
‘Declare and instantiate an object variable of the report class
Dim MyReport As New CrystalReport1
Step 2: Modify the properties of the report object.
After instantiating the report variable, all the properties and methods of the ReportDocument object are available to you. Set the properties that need to be modified. The different properties of the report object are explained throughout all the chapters in Part II of this book.
In this example, the record selection formula is changed. The report variable MyReport (from Step 1) is used to get a reference to the DataDefinition object and change its RecordSelectionFormula property.
MyReport.DataDefinition.RecordSelectionFormula = "{Orders.Order Date}>#01/01/2004#"
The above line of code is very simplistic for purposes of illustrating how to change a property of the ReportDocument class. Real applications aren’t so simple and you can save yourself effort by using the same code in different projects. A lot of times you will find yourself taking a specific piece of code and rewriting it so that it can be generic enough to be used by multiple applications or put into a reporting library. In many of the examples in this book, I do this by writing a method that gets passed the report object and any necessary parameters. The method modifies the properties of the report object that was passed to it and exits. In these examples, the method’s code will be shown but not the code that calls it. I won’t repeat the code that declares and initializes the report variable and previews it. It is assumed that you know that these methods should be called after the report object is declared and initialized, but before previewing the report. If you need a refresher, you can refer back to this chapter.
Step 3: Print or preview the report.
To preview the report, pass the report variable to the viewer control. To print the report, call the PrintToPrinter() method of the report variable. To be consistent, my examples always preview the report. Thus, the sample code will assign the report variable to the viewer control.
CrystalReportViewer1.ReportSource = MyReport
Those three steps are always used for performing runtime customization. The next question you might be asking yourself is where to put the code. You can put this code anywhere in the form. If you want to load the form immediately and customize the report, put it in the Load() event. There are time that you are going to call this code after the user has entered various data that specifies how the report should be customized. If that’s the case, put the code in response to the click event of an OK button that confirms they are finished inputting data. The following code sample shows you a complete code listing and it demonstrates where to put the code for calling a generic method to modify report properties.
Private Sub Form1_Load(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles MyBase.Load
Dim MyReport As New CrystalReport1
‘Call all report modification code here.
‘As mentioned in Step 2 above, this can be a method that is passed the report variable.
‘For illustration purposes, I’m calling a generic method that changes
‘the report title. The code for ModifyTitle() isn’t shown here.
ModifyTitle(MyReport, “Runtime Demo”)
CrystalReportViewer1.ReportSource = MyReport
End Sub
If you are writing an ASP.NET application, this code can be put in the Page_Load() event. See Chapter 3 for a more information about writing ASP.NET applications.
Note |
All report customization must be done prior to previewing or printing the report. No changes are allowed once the report is generated. For example, you can’t use .NET to change the way a field is formatted depending upon the current group value. Making dynamic report changes while the report is running requires writing formulas and using conditional formatting (discussed in Chapters 7, 8, and 9). |
ASP.NET Template
Printing reports within an ASP.NET application requires a slightly different template than the Windows template shown in Listing 14-1. Reports shown on an ASP.NET page are unique in that each time the user moves to a new report page, the web page gets reloaded. If a report is resource intensive, then this can slow performance or tie up resources. For example, a report that connects to SQL Server will open a new connection each time the page is loaded. Fixing this problem requires saving the report to the Session collection and using the IsPostBack function. When doing ASP.NET development, use the template in Listing 14-2 for optimizing reports.
Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
Dim MyReport As CrystalDecisions.CrystalReports.Engine.ReportDocument
If Not IsPostBack Then
MyReport = New CrystalReport1
‘Call all report modification code here.
‘For illustration purposes, I’m calling a generic method that changes
‘the report title. The code for ModifyTitle() isn’t shown here.
ModifyTitle(MyReport, “Runtime Demo”)
Session("MyReport") = MyReport
Else
‘Get the report object from the Session collection
MyReport = CType(Session("MyReport"), CrystalDecisions.CrystalReports.Engine.ReportDocument)
End If
CrystalReportViewer1.ReportSource = MyReport
End Sub
If a report connects to an MS Access database, you might notice that the record lock file (.MDL) isn’t released when the page unloads. If the report connects to SQL Server, then it leaves the connection open. This is because the connection is closed when the report is disposed of during .NET’s garbage collection process. To dispose of an object when the page unloads, you have to force garbage collection during the Unload() event. Listing 14-3 shows how this is done.
Listing 14-3. Forcing garbage collection in an ASP.NET page.
Private Sub Page_Unload(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.Unload
MyReport.Close()
MyReport.Dispose()
GC.Collect()
End Sub
There are a couple points of interest here. The first assumes that the MyReport object variable has been declared at the class level. The template shown in Listing 14-2 declares the MyReport object variable at the procedure level. This declaration will have to be moved to the class level. The second point is that if you are connecting to SQL Server and using VB.NET, this code doesn’t work. Disposing the report object closes the connection to the database and you have to login again (thus defeating the purpose of Listing 14-2). You have to clean up resources on another web page. If you are using the C# listings shown at the end of this chapter, this code will work for SQL Server.