How can I display Crystal XI reports inside a Delphi 2007 application?
The most recent Crystal XI component for Delphi was released for Delphi 7. That VCL component compiles in D2007, but gives me errors at runtime. What is the best way to display a database-connected Crystal Report in a Delphi 2007 application?
This is the solution I've found, using ActiveX:
First, register the Active X control like this:
In Delphi, choose Component -> Import Component
Click on "Type Library", click Next
Choose "Crystal ActiveX Report Viewer Library 11.5"
Pick whatever Palette Page you want (I went with "Data Access")
Choose an import location
Exit out of the wizard
Add the location you chose to your project Search Path
Now this code should work:
...
uses
CrystalActiveXReportViewerLib11_5_TLB, OleAuto;
...
procedure TForm1.Button1Click(Sender: TObject);
var
cry : TCrystalActiveXReportViewer;
oRpt, oApp : variant;
i : integer;
frm : TForm;
begin
cry := TCrystalActiveXReportViewer.Create(Self);
oApp := CreateOleObject('CrystalRuntime.Application');
oRpt := oApp.OpenReport('c:\my_report.rpt',1);
for i := 1 to oRpt.Database.Tables.Count do begin
oRpt.Database.Tables[i].ConnectionProperties.Item['User ID'] := 'username';
oRpt.Database.Tables[i].ConnectionProperties.Item['Password'] := 'password';
end;
frm := TForm.Create(Self);
try
cry.Parent := frm;
cry.Align := alClient;
cry.ReportSource := oRpt;
cry.ViewReport;
frm.Position := poOwnerFormCenter;
frm.ShowModal;
finally
FreeAndNil(frm);
end; //try-finally
end;
procedure TForm1.btnExportClick(Sender: TObject);
var
cry : TCrystalActiveXReportViewer;
oRpt, oApp : variant;
i : integer;
begin
//Export the report to a file
cry := TCrystalActiveXReportViewer.Create(Self);
oApp := CreateOleObject('CrystalRuntime.Application');
oRpt := oApp.OpenReport(c_DBRpt,1);
for i := 1 to oRpt.Database.Tables.Count do begin
oRpt.Database.Tables[i].ConnectionProperties.Item['User ID'] := 'username';
oRpt.Database.Tables[i].ConnectionProperties.Item['Password'] := 'password';
end;
oRpt.ExportOptions.FormatType := 29; //excel 8
oRpt.ExportOptions.DiskFileName := 'c:\output.xls';
oRpt.ExportOptions.DestinationType := 1; //file destination
//Export(False) => do NOT prompt.
//Export(True) will give runtime prompts for export options.
oRpt.Export(False);
end;
If you use this method, then this (rather dense) reference will be helpful, especially since Intellisense doesn't work on Ole objects like these.
Edit: The original link to the reference broke, so I changed it to point to a new one (valid as of Dec 15 2009). If that new one breaks, then Google should be able to find it.
I know it's not your question and it might not be an acceptable answer at all in your situation, but I have found FastReports to be clearly superior to Crystal for my purposes. It's lighter weight, includes a real scripting language, incorporates event handling, can make calls into your native code for information and updates and does not require an ActiveX connection. I can export my reports into sharp looking PDF files or Excel spreadsheets and several other formats. The quality of the output adds to the overall experience users get from my application. I could go on, but if it's off topic for you, it won't be helpful.
For the sake of anyone else who can use it, here is a complete class that gives a pleasant wrapper around these vile Crystal interactions. It works for me about 80% of the time, but I suspect a lot of this stuff is very dependent on the specific platform on which it runs. I'll post improvements as I make them.
Somebody at Business Objects should really take a hard look at this API. It sucks pretty badly.
{
Class to facilitate the display of Crystal 11 Reports.
The Crystal 11 VCL component does not seem to work with Delphi 2007.
As a result, we have to use ActiveX objects, which make deployment messy.
This class is similar to CrystalReporter, but it works for Crystal 11.
However, it lacks some of the features of the old CrystalReporter.
Refer to the crystal reports activex technical reference to duplicate the
missing functionality.
Example usage is at the bottom of this unit.
//}
unit CrystalReporter11;
interface
uses
CrystalActiveXReportViewerLib11_5_TLB, OleAuto, Classes, Controls;
type
TCryExportFormat = (
XLS
,PDF
);
type
TCrystalReporter11 = class
private
FCryRpt : TCrystalActiveXReportViewer;
FRpt, FApp : variant;
FReportFile, FUsername, FPassword, FServer, FFilters : string;
FOwner : TComponent;
procedure SetLoginInfo(const username, password, server : string);
function GetFilterConds: string;
procedure SetFilterConds(const Value: string);
public
property FilterConditions : string read GetFilterConds write SetFilterConds;
procedure ExportToFile(ExportFileName : string;
FileExportFmt : TCryExportFormat; PromptForOptions : boolean);
procedure Display;
constructor Create(AOwner : TComponent; ReportFile : string); overload;
constructor Create(AOwner : TComponent; ReportFile,
Username, Password, Server : string); overload;
end;
implementation
uses
SysUtils, Forms;
const
//these are taken from pgs 246 and 247 of the technical reference
c_FmtCode_Excel = 29;
c_FmtCode_PDF = 31;
constructor TCrystalReporter11.Create(AOwner: TComponent; ReportFile: string);
begin
inherited Create;
try
FReportFile := ReportFile;
if FileExists(FReportFile) then begin
FOwner := AOwner;
FCryRpt := TCrystalActiveXReportViewer.Create(AOwner);
FApp := CreateOleObject('CrystalRuntime.Application');
FRpt := FApp.OpenReport(FReportFile,1);
FFilters := FRpt.RecordSelectionFormula;
end
else begin
raise Exception.Create('Report file ' + ReportFile + ' not found!');
end;
except on e : exception do
raise;
end; //try-except
end;
constructor TCrystalReporter11.Create(AOwner: TComponent; ReportFile, Username,
Password, Server: string);
begin
Create(AOwner,ReportFile);
FUsername := Username;
FPassword := Password;
FServer := Server;
SetLoginInfo(FUsername,FPassword,FServer);
end;
procedure TCrystalReporter11.Display;
var
rptForm : TForm;
begin
SetLoginInfo(FUsername,FPassword,FServer);
FCryRpt.ReportSource := FRpt;
rptForm := TForm.Create(FOwner);
try
FCryRpt.Parent := rptForm;
FCryRpt.Align := alClient;
FCryRpt.ViewReport;
rptForm.Position := poOwnerFormCenter;
rptForm.WindowState := wsMaximized;
rptForm.Caption := ExtractFileName(FReportFile);
rptForm.ShowModal;
finally
FreeAndNil(rptForm);
end; //try-finally
end;
procedure TCrystalReporter11.ExportToFile(ExportFileName : string;
FileExportFmt : TCryExportFormat; PromptForOptions : boolean);
begin
case FileExportFmt of
XLS : FRpt.ExportOptions.FormatType := c_FmtCode_Excel;
PDF : FRpt.ExportOptions.FormatType := c_FmtCode_PDF;
end; //case
FRpt.ExportOptions.DiskFileName := ExportFileName;
FRpt.ExportOptions.DestinationType := 1; //file destination
FCryRpt.ReportSource := FRpt;
FRpt.Export(PromptForOptions);
end;
function TCrystalReporter11.GetFilterConds: string;
begin
Result := FFilters;
end;
procedure TCrystalReporter11.SetFilterConds(const Value: string);
begin
FFilters := Value;
if 0 < Length(Trim(FFilters)) then begin
FRpt.RecordSelectionFormula := Value;
end;
end;
procedure TCrystalReporter11.SetLoginInfo(const username, password,
server : string);
var
i : integer;
begin
//set user name and password
//crystal only accepts these values if they are CONST params
for i := 1 to FRpt.Database.Tables.Count do begin
FRpt.Database.Tables[i].ConnectionProperties.Item['User ID'] := username;
FRpt.Database.Tables[i].ConnectionProperties.Item['Password'] := password;
try
{
Some reports use direct connections, and others use an ODBC Data Source.
Crystal XI uses a different label to refer to the database name in each
method.
I don't know how to determine in advance which method is being used, so:
First, we try the direct connection.
If that fails, we try the "data source" method.
Reference: "Crystal Reports XI Technical Reference", pages 41 thru 46;
"Common ConnectionProperties"
}
FRpt.Database.Tables[i].ConnectionProperties.Item['Server'] := server;
except on E: Exception do
FRpt.Database.Tables[i].ConnectionProperties.Item['Data Source'] := server;
end;
end;
end;
{
Example usage:
procedure TForm1.btnShowRptDBClick(Sender: TObject);
var
cry : TCrystalReporter11;
begin
cry := TCrystalReporter11.Create(Self,'c:\my_report.rpt','username',
'password','server.domain.com');
try
cry.Display;
finally
FreeAndNil(cry);
end;
end;
}
end.
I too have been disappointed with the lack of effort by Crystal Reports with respect to application integration. I use the RDC, and from what I understand this is being deprecated and emphasis is being placed on .Net.
My application has these files in the uses clause: CRRDC, CRAXDRT_TLB,
It works ok. The because drawback is parameter passing. In my option the parameter dialog boxes which come with the viewer are terrible. So I use my own Delphi application to prompt for parameters and pass them to the report.