Crystal Reports Online Training

Learn Online, Anytime, Anywhere

Step-by-step online tutorials.

15.03 Code Listings for Mapping the Report Classes

The sample program is a Windows program that uses a listbox, lstMapping, for the output. Each property is printed as a string in the listbox. Indentation is used to offset the object from its properties so that it is easier to read. Notice that all the parameters and variables are declared using the full namespace. You can see that this is quite lengthy and that the code could be shortened considerably by using the VB.NET Imports (or C# using) statement at the beginning of the program. However, in this book I always use the full namespace for learning purposes. The program also uses a generic Output() procedure for printing this information. This makes it easy for you to modify the program so that it can fit your needs. You could modify the code to save it an XML file or print to a console window.

Listing 15-4. Mapping the ReportDefinition object model.
[VB.NET]
Private Sub Form1_Load(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles MyBase.Load
Dim myReport As CrystalDecisions.CrystalReports.Engine.ReportDocument
myReport = New CrystalReport1()
MapAreas(myReport.ReportDefinition.Areas, 0)
End Sub
Sub MapAreas(ByVal Areas As _
CrystalDecisions.CrystalReports.Engine.Areas, ByVal Indent As Integer)
Dim Area As CrystalDecisions.CrystalReports.Engine.Area
For Each Area In Areas
MapArea(Area, Indent)
Next
End Sub
Sub MapSections(ByVal Sections As _
CrystalDecisions.CrystalReports.Engine.Sections, ByVal Indent As Integer)
Dim Section As CrystalDecisions.CrystalReports.Engine.Section
For Each Section In Sections
MapSection(Section, Indent)
Next
End Sub
Sub MapReportObjects(ByVal ReportObjects As _
CrystalDecisions.CrystalReports.Engine.ReportObjects, _
ByVal Indent As Integer)
Dim ReportObject As CrystalDecisions.CrystalReports.Engine.ReportObject
For Each ReportObject In ReportObjects
MapReportObject(ReportObject, Indent + 2)
Next
End Sub
Sub MapArea(ByVal Area As CrystalDecisions.CrystalReports.Engine.Area, _
ByVal Indent As Integer)
Dim Section As CrystalDecisions.CrystalReports.Engine.Section
'Print the Area properties
Output("Area: " & Area.Name, Indent)
Output("Kind: " & Area.Kind.ToString, Indent + 2)
Output("Suppress: " & Area.AreaFormat.EnableSuppress.ToString, Indent + 2)
Output("Keep Together: " & Area.AreaFormat.EnableKeepTogether.ToString, _
Indent + 2)
For Each Section In Area.Sections
MapSection(Section, Indent + 2)
Next
End Sub
Sub MapSection(ByVal Section As _
CrystalDecisions.CrystalReports.Engine.Section, ByVal Indent As Integer)
Dim ReportObject As CrystalDecisions.CrystalReports.Engine.ReportObject
'Print the Section properties
Output("Section: " & Section.Name, Indent)
Output("Suppress: " & _
Section.SectionFormat.EnableSuppress.ToString, Indent + 2)
Output("Keep Together: " & _
Section.SectionFormat.EnableKeepTogether.ToString, Indent + 2)
For Each ReportObject In Section.ReportObjects
MapReportObject(ReportObject, Indent + 2)
Next
End Sub
Sub MapReportObject(ByVal ReportObject As _
CrystalDecisions.CrystalReports.Engine.ReportObject, _
ByVal Indent As Integer)
'Explicitly cast the ReportObject as its native data type
Select Case ReportObject.Kind
Case CrystalDecisions.[Shared].ReportObjectKind.TextObject
MapTextObject(CType(ReportObject, _
CrystalDecisions.CrystalReports.Engine.TextObject), Indent)
Case CrystalDecisions.[Shared].ReportObjectKind.FieldObject
MapFieldObject(CType(ReportObject, _
CrystalDecisions.CrystalReports.Engine.FieldObject), Indent)
End Select
End Sub
Sub MapTextObject(ByVal TextObject As _
CrystalDecisions.CrystalReports.Engine.TextObject, _
ByVal Indent As Integer)
Output("Name: " & TextObject.Name, Indent)
Output("Kind: " & TextObject.Kind.ToString(), Indent + 2)
Output("Text: " & TextObject.Text, Indent + 2)
End Sub
Sub MapFieldObject(ByVal FieldObject As _
CrystalDecisions.CrystalReports.Engine.FieldObject, _
ByVal Indent As Integer)
Output("Name: " & FieldObject.Name, Indent)
Output("Kind: " & FieldObject.Kind.ToString(), Indent + 2)
Output("DataSource.Name: " & FieldObject.DataSource.Name, Indent + 2)
Output("DataSource.Formula: " & FieldObject.DataSource.FormulaName, _
Indent + 2)
End Sub
Sub Output(ByVal Line As String, ByVal Indent As Integer)
lstMapping.Items.Add(New String(" "c, Indent) & Line)
End Sub
[C#]
private void Form1_Load(object sender, System.EventArgs e)
{
CrystalReport1 myReport = new CrystalReport1();
MapAreas(myReport.ReportDefinition.Areas, 0);
}
public void MapAreas(CrystalDecisions.CrystalReports.Engine.Areas Areas, int Indent)
{
foreach(CrystalDecisions.CrystalReports.Engine.Area Area in Areas)
{
MapArea(Area, Indent);
}
}
public void MapSections(CrystalDecisions.CrystalReports.Engine.Sections Sections, int Indent)
{
foreach(CrystalDecisions.CrystalReports.Engine.Section Section in Sections)
{
MapSection(Section, Indent);
}
}
public void MapReportObjects(CrystalDecisions.CrystalReports.Engine.ReportObjects ReportObjects, int Indent)
{
foreach(CrystalDecisions.CrystalReports.Engine.ReportObject ReportObject in ReportObjects)
{
MapReportObject(ReportObject, Indent + 2);
}
}
public void MapArea(CrystalDecisions.CrystalReports.Engine.Area Area, int Indent)
{
Output("Area: " + Area.Name, Indent);
Output("Kind: " + Area.Kind.ToString(), Indent+2);
Output("Suppress: " + Area.AreaFormat.EnableSuppress.ToString(), Indent + 2);
Output("Keep Together: " + Area.AreaFormat.EnableKeepTogether.ToString(), Indent + 2);
foreach(CrystalDecisions.CrystalReports.Engine.Section Section in Area.Sections)
{
MapSection(Section, Indent + 2);
}
}
public void MapSection(CrystalDecisions.CrystalReports.Engine.Section Section, int Indent)
{
//Print the Section properties
Output("Section: " + Section.Name, Indent);
Output("Suppress: " + Section.SectionFormat.EnableSuppress.ToString(), Indent);
Output("Keep Together: " + Section.SectionFormat.EnableKeepTogether.ToString(), Indent);
foreach(CrystalDecisions.CrystalReports.Engine.ReportObject ReportObject in Section.ReportObjects)
{
MapReportObject(ReportObject, Indent + 2);
}
}
public void MapReportObject(CrystalDecisions.CrystalReports.Engine.ReportObject ReportObject, int Indent)
{
switch(ReportObject.Kind)
{
case CrystalDecisions.Shared.ReportObjectKind.TextObject:
{
MapTextObject(((CrystalDecisions.CrystalReports.Engine.TextObject)ReportObject), Indent);
break;
}
case CrystalDecisions.Shared.ReportObjectKind.FieldObject:
{
MapFieldObject(((CrystalDecisions.CrystalReports.Engine.FieldObject)ReportObject), Indent);
break;
}
}
}
public void MapTextObject(CrystalDecisions.CrystalReports.Engine.TextObject TextObject, int Indent)
{
Output("Name: " + TextObject.Name, Indent);
Output("Kind: " + TextObject.Kind.ToString(), Indent + 2);
Output("Text: " + TextObject.Text, Indent + 2);
}
public void MapFieldObject(CrystalDecisions.CrystalReports.Engine.FieldObject FieldObject, int Indent)
{
Output("Name: " + FieldObject.Name, Indent);
Output("Kind: " + FieldObject.Kind.ToString(), Indent + 2);
Output("DataSource.Name: " + FieldObject.DataSource.Name, Indent + 2);
Output("DataSource.Formula: " + FieldObject.DataSource.FormulaName, Indent + 2);
}
public void Output(string Line, int Indent)
{
lstMapping.Items.Add(new string(' ',Indent) + Line);
}

Most of this code is pretty simple. It consists of procedures that use For Each loops to traverse a collection. While traversing the collection, the object’s properties are printed and if there is a sub-collection then that is traversed as well.

There are some interesting aspects of the code that need to be explored. Notice that there are three main mapping procedures: MapAreas, MapSections, and MapReportObjects. These directly correspond to the three distinct collections of the ReportDefinition class. The top-most collection is the Areas collection. This collection can be used to reference the Sections collection, which is used to reference the ReportObjects collection. But when you look at the object model previously shown in Figure 15-2, these collections each have their own property in the ReportDefinition class. Thus, you can also go directly to the Sections or ReportObjects collections.

This example uses the form’s Load() event to call the MapAreas() procedure so that you can see how the entire object hierarchy is mapped out. But you can isolate the individual collections by replacing the MapAreas() call with a call to either MapSections() or MapReportObjects().

The MapReportObject() procedure (near the end of the listing) also deserves mentioning. The ReportObject class isn’t used to represent any actual objects on your report. Instead, it is the base class for those objects. You have to explicitly cast the object as the proper data type before working with it. The actual report objects are shown in Figure 15-3. They are all very similar to each other, and some properties are even repeated. But each one has at least a few properties that make it unique from the other classes.





Figure 15-3. The report classes that inherit from the ReportObject class.

When mapping out the report objects, there is no immediate way to know what type of object is being referenced. Fortunately, the ReportObject class has a property that identifies this. The Kind property tells you what type of object it is. The MapReporObject() procedure uses a Select Case statement to determine what type of report object it is and then calls the proper mapping procedure. When it calls the mapping procedure, the report object variable is cast as the proper class within the parameter list. This listing only shows the code for mapping the TextObject and the FieldObject, but it is simple to add the other objects if you wish.

Let’s run this program on a sample report. I made a few changes to the label report from Chapter 6. The names of the sections were changed so that they are descriptive. By default, when you create a report the section names are given generic names such as Section1, Section2, etc. Since the Crystal Reports designer prints a section description next to the section name, this normally isn’t a problem. But the mapping example prints out raw text and doesn’t have the designer available, so the names were changed to make it easier to read the output. The changes to the section names are shown in Figure 15-4. The area names can’t be changed, so they will always use the generic naming convention.





Figure 15-4. The report designer for the labels report.

This example is fairly simple so that most of the important output can be shown here. Most of the sections simply show a text box that represents a field of the customer’s address. The different headers and footers are suppressed so that only the Detail sections are shown. There are three Detail sections and they are all part of the same area. Thus, the output should show a single Area object with three detail sections listed.

The section DetailBottom is a text field that is composed of three fields and a comma to separate the City from the Region. When you look at this field in the example’s output, it only shows a comma. Thus, the actual composition of the field is stored in an internal format that you can’t access. This is shown near the bottom of the output.





Figure 15-5. Mapping the objects of the the label report.