(Print this page)

Feeding Crystal Reports from your application
Published date: Friday, September 1, 2006
On: Moer and Éric Moreau's web site

This month, I decided to reveal some secrets about Crystal Reports. In fact, I have decided to write it because this article will answer many questions found into newsgroups.

This column will not show you how to place controls on a report, how to format fields, neither how to distribute reports. The topics have been narrowed to the communication between your current application and the reports you are building.

The version that is used here is the version of Crystal Reports that comes bundled with Visual Studio 2005.

Which version of Crystal Reports do we have?

The version that comes bundled into Visual Studio 2005 (Professional edition and above) is a special trimmed down version of the version 10 (something like 10.2). If you think that some features are missing, you can have a look at here to find what the full version has more to offer. This version includes a customized designer that is fully integrated in Visual Studio Visual Studio 2005 for report design. You may use the integrated designer to create reports for use in your Visual Studio 2005 applications. If these applications are Windows applications, the runtime can be distributed freely to any number of end user’s PC. It is a bit different for Web apps. You can read full licensing details from here. I really think that for most of your needs, the bundled version will be more then enough.

Planning your report

Before going to create your report, you need to answer a couple of questions. Some of these questions:

  • Can Crystal Reports connect to my data source?
  • Is my presentation layer is having a direct access to the database or does it has to go through a data layer?
  • Do you need to add additional data (calculation or transformation) to what you find in your database?
  • Do I prefer to feed my report from a class instance already loaded?
There are many ways of passing data to a report. My favorite method of all time has always been to pass a full dataset (a recordset in the DAO/ADO era) to the report. This is the method that will be used in this column. This method let us create on-the-fly data to be printed. It also let us modifies or summarizes our main source. Very often, we already have a dataset in memory containing the data we want to print so why re-query the database? If you prefer to connect your reports directly to your database, you may still find other sections of this article interesting because everything else is the same.

Preparing data for the report

As I said earlier, my favorite method is to feed a report from a dataset I already have or that I create on the fly. Some extra steps are required in order to do it this way like creating a schema for the dataset.

So my demo application has a button titled “Create data source”. This button creates a dataset that contains 2 datatables (one for countries and one for states), load data into both datatables and create a relation between these two tables. All this is done without any database (the idea here is to get a dataset no matter how it is filled).

You now need to create a schema from this dataset. The easiest way to do it is to use the WriteXMLSchema method from your dataset. This is what the button titled “Create schema” is doing:

mdsData.WriteXmlSchema("c:\Temp\DemoSchema.xsd")

Embedded or not?

You now have another decision to make. This decision is not a risky one as it can be easily modify later. The reports that you are creating with the designer can either be distributed as a standalone report (.rpt) file or as compiled (aka embedded) within your project (.exe or .dll).

By default, reports that you add to a project are added as embedded resource (see figure 1). If you want to use standalone report files, change the “build action” property to None.

Figure 1: Embedded or not?

The advantage of an embedded report is that you don’t have a series of report files to distribute and the end users cannot modify them. The disadvantage is that if a report needed to be modified, the project that embeds the report needs to be recompiled and redeployed.

Personally, I prefer to distribute the report files as standalone files (in fact, I simply copy the report files on a server and all applications are loading the report from there). If you need to change a small thing like a font or a size, you don’t need to build and publish, you just have to save and copy. Users don’t even need to stop the application and restart it in order to get your modification.

Creating a project for your reports

Another hint: instead of adding my reports to the same project as all my classes, I always create a new project that only contains my reports and the schemas. I also remove the build property from the Configuration Manager so that project is never compiled.

I normally use the “Crystal Reports Application” template for this particular project as all the required references are automatically added. I normally open the Configuration Manager dialog (from the Build menu) and remove the Build option for this project (see figure 2).

Figure 2: Removing the build option

To this new project, add the schema you created in the previous step. This can be done by drag and dropping (from Windows Explorer to your project name) or by using “Add + Existing item…” on your project.

Adding a report to your project

Now that we have the data ready and the schema, we can now create the report. To get the Crystal Reports designer, you need to add a report to a project in your solution. This can be easily done by using the “Add New Item…” from the “Project” menu. The “Add New Item” dialog will then be shown to you (see figure 3). From the list of templates, select “Crystal Report”, change the Name to something meaningful (I have set it to rptTest.rpt) and click the “Add” button.

Figure 3: Adding a report to a project

Then the “Crystal Reports Gallery” wizard will start and ask you for the type of report to create. Accepts the default options (Using the report wizard and Standard) and click the OK button.

Then the “Standard Report Creation Wizard” will be shown. That’s where you need to bind your current report to your data source or to your schema. In an earlier step, we have prepared a XML schema. This schema has been added to the same project that contains the report. This schema is now available under “Project Data – ADO.NET DataSets” (see figure 4). Using the arrows in the middle of the dialog, add both tables (countries and states) to the list of selected tables.

Figure 4: Selecting your data source

After having clicked the Next button, you will get the wizard will get you to the Link step. In this case, you simply need to click the Next button because we have already done our relation through the code and the XML schema contains it.

In the Fields step of the wizard, simply add all fields to the list of “Fields to Display” and click the Next button.

In the Grouping step, select “Countries.Description” as the field on which to group by and click the Finish button.

The Crystal Reports designer will finally display your report (see figure 5). It would now be the perfect time to set the “Build Action” property to None.

Figure 5: The report designer

Testing your report

Before starting to customize the report, I always like to test it. Our main project, the one that includes a form, is not ready yet to test the report. You first need to add a viewer to the main application. From your toolbox, find the Crystal Reports tab, select the CrystalReportViewer (see figure 6) and place one on your form.

Figure 6: Adding a viewer to the form

You now need to write a couple of lines of code in order to load a report, pass a dataset to the report and display it into the viewer. This is the code you need:

'Load the standalone report
mrptDoc.Load(Forms.Application.StartupPath & "\rptTest.rpt")
'Pass the dataset to the report
mrptDoc.SetDataSource(mdsData)
'Pass the Report Document object to the viewer
CrystalReportViewer1.ReportSource = mrptDoc
You also need to define your mrptDoc variable at the top of your class like this:
'Implies you have imported CrystalDecisions.CrystalReports.Engine
Private mrptDoc As New ReportDocument
But wait before running the sample application! If you carefully look at the call to the Load method of the ReportDocument object, you will see that the application is expecting to find a file named rptTest.rpt in the startup folder (usually bin\debug at this time). You can set this path to be anywhere but the point is that you need to copy your standalone report files to that folder! Otherwise, a load report exception will be raised. You also need to insure that the dataset is filled otherwise you will get a login prompt on the screen. If you do these 2 requirements, you should see your report coming to viewer with the data created manually.

Now that the report is shown correctly, you can now customize the report to be more good looking. One really nice thing about using standalone reports is that you can edit your reports using the designer of Visual Studio without having to stop your application, save it, recopy it to the right folder and finally get back to your application to press the preview button. That’s make the process of testing changes to reports a lot faster then having to recompile the application each time!

Passing a value to a report object

It is really easy to feed an object like the ITextObject with a value from your application. I often do that for report title (I am often able to use the same report layout as the basis for many final reports and then I need a way to print a title dynamically on the report). The trick here is to use the ReportsObject collection to access one element of the report and give a value to it.

Before looking at the code that does that, add a ITextObject to your report (by right-clicking the report, selecting Insert, then Text Object and clicking on your report to position it. Like any other object, because you want to access through code, you should give this object a significant name by setting the (Name) property (something like txtTitle).

Here is a helper method that can be used to set the Text property of any ITextObject of a loaded report:

Private Sub ApplyTextObject(ByVal vstrTextObject As String, _
    ByVal vstrTextValue As String)
    Try
        CType(mrptDoc.ReportDefinition.ReportObjects(vstrTextObject), _
CrystalDecisions.CrystalReports.Engine.TextObject).Text = _
vstrTextValue
    Catch ex As Exception
        MessageBox.Show("ApplyTextObject" & Environment.NewLine & _
"There is no >>" & vstrTextObject & _
"<< object in this report!")
    End Try
End Sub
This method can now be called like this before setting the ReportSource of your viewer: ApplyTextObject("txtTitle", "Your title goes here")

The first parameter is the name of the object found in your report. The second parameter is the value to give to this object. This method is currently setting the Text property but just about any property could be set.

The downloadable demo even contains a similar method that can be used to set a ITextObject from a sub-report (the method is named ApplySubReportTextObject). When you call this method, you need to specify the name of the sub-report in addition to the name of the object and the value.

Passing a value to a parameter of the report

You might also want to pass values to a report that comes from the user interface or a database (or any other source) and use that value to filter or format your report. For example, we will add a threshold parameter to the report and use it to set the BackColor property of the population field.

We first need to add a parameter to our report. To do this, insure that your report is currently in the designer and that you can see the “Field Explorer” of Crystal Reports (if you don’t see it, select the Crystal Reports menu and pick Field Explorer). In the Field Explorer, right-click “Parameter Fields” and select New. A dialog titled “Create Parameter Field” will be displayed (see figure 7). In this dialog, set the Name field to something meaningful (like PopulationThreshold) and select the correct value type (a number in this case). When you are done, click the OK button.

Figure 7: Creating a parameter field

We will now use this parameter to change the BackColor of the Population field. Start by right-clicking the Population field on your report and selecting “Format object” from the context-menu. From the Format Editor, open to the Border tab. To the right of “Background” there is a button (on which you can see X+2) that allows you to enter a formula, click on it. In the bottom pane of the Formula Workshop dialog (see figure 8), enter this formula (this formula sets the background color to red if the value of the Population field is higher then the parameter value otherwise, the background is set to white) and click the “Save and close” button:

if {States.Population} > {?PopulationThreshold} then
    crRed
else
    crWhite

Figure 8: Entering a formula

The last thing you need to do is to pass a value to the parameter. A single line of code is required to do that. This line must be called before setting the ReportSource of your viewer like this:

mrptDoc.SetParameterValue("PopulationThreshold", 5000000)
The first parameter is the name of the parameter found in your report. The second parameter is the value to give to this parameter.

If you run your application again (don’t forget to copy your report), you should be able to see that some states have there background color red.

Free Walkthroughs

Business Objects (the company behind Crystal Reports) created a 591 pages walkthroughs that can be downloaded for free from here. The samples of these walkthroughs are also available for free download from here. I strongly suggest you download these.

Conclusion

I really think that this article gives you a fast (but complete) getting started method to create your first reports with custom data along with simple methods of setting objects properties and parameters. You can go a lot deeper in Crystal Reports as it is a huge product.

I hope you appreciated the topic and see you next month.


(Print this page)