Getting started with Reports (Delphi)
Overview
A really simple demo on how to create an Excel report. No database is
used, only report variables.
Concepts
A template is an Excel file with tags that will be replaced by
report variables or fields from a dataset. Tags are always on the
form <#tag> If a tag has parameters, it always has the form
<#tag(param1; param2...)>. You can read the FlexCel Reports Tag Reference.
for more information.
To set the value for report variable, use
TFlexCelReport.SetValue. You can set any kind of object from
here, not just text. If you set it to a TFormula object, you
will enter a formula.
<#Tags> will be replaced inside Cells, Comments, Sheet
names, Images, Hyperlinks, AutoShapes, Headers
and Footers. All of this is shown here. To see the
headers and footers, you must do a print preview.
There are 2 special datatypes you must be aware:
Dates: Dates in Excel are just numbers, with a special cell
format. To enter a date in Excel, make sure the cell has a date
format.
Multiline Text:To be able to show carriage return on a cell,
it must have "Wrap text" enabled on its cell format.
Hyperlinks are a special case for 2 reasons:
You can't enter
<# into an hyperlink, as # is a reserved keyword. So, tags in
hyperlinks are on the form "*.TAG.*" (On older FlexCelVersions
we would use "<.tag>". This will still work, but you can't
enter this text into Excel 2003 or newer)
If you do not begin the
hyperlink text with "http://" or "https://" while
creating the link in Excel, it will be created as local file. As
this is not what you would normally want, all "local file"
hyperlinks will be changed to URL hyperlinks if the replaced
text starts with "http:" or "https:". So do not create links like
"www.tmssoftware.com", make your
hyperlinks "https://www.tmssoftware.com"
To create an hyperlink to a cell in the same file using tags, start
the definition of the hyperlink with a "#", like
"*.sheetvar.*!*.#cellvar.*"
There are special tags, like the <#If( condition, iftrue,
iffalse)> that might contain other tags inside. You can use any
composition of tags on any of the places where they will be
replaced.
Empty comments will be removed. This is a feature so you can
selectively comment cells based on the comment text. If comment
text is blank, no comment will be made.
To replace images, name them as <#tag>. To see or change the
name of an image, use the combo box at the upper left corner on
Excel.
You can use tags as usual on sheet names. But, as the maximum sheet
name length is 32, it is kind of limited. You can use
Equal tag (<#=(cell)>) to solve this limitation.
You can define reusable variables on the config sheet. For
example, here we define an expression containing the name and
"anonymous" if the name is null, and we use it on the sheet name
and the print header. Then we can use this expression on many
places. <#=(cell)> is conceptually the same as report
expressions, but report expressions are normally cleaner on what
they mean.
Rich text will be preserved on cells. If for example you write
"<#name> and <#date>", the result will be "your name and
thedate"
Files
UMainForm.pas
unit UMainForm;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics,
FlexCel.VCLSupport, FlexCel.Core, FlexCel.XlsAdapter, FlexCel.Report,
{$if CompilerVersion >= 23.0} System.UITypes, {$IFEND}
ShellApi,
Controls, Forms, Dialogs, StdCtrls, ExtCtrls;
type
TMainForm = class(TForm)
edName: TLabeledEdit;
edURL: TLabeledEdit;
cbAutoOpen: TCheckBox;
btnCancel: TButton;
btnGo: TButton;
SaveDialog: TSaveDialog;
procedure btnCancelClick(Sender: TObject);
procedure btnGoClick(Sender: TObject);
private
procedure Setup(const ReportStart: TFlexCelReport; const UserName, UserUrl,
DataPath: string);
procedure AutoOpenRun;
procedure NormalRun;
function GetDataPath: string;
{ Private declarations }
public
{ Public declarations }
end;
var
MainForm: TMainForm;
implementation
uses
{$if CompilerVersion >= 28.0} Threading, {$IFEND}
IOUtils;
{$R *.dfm}
procedure TMainForm.btnCancelClick(Sender: TObject);
begin
Close;
end;
procedure TMainForm.btnGoClick(Sender: TObject);
begin
if cbAutoOpen.Checked then AutoOpenRun else NormalRun;
end;
function TMainForm.GetDataPath: string;
begin
Result := TPath.Combine(TPath.GetDirectoryName(ParamStr(0)), '..\..');
end;
procedure TMainForm.Setup(const ReportStart: TFlexCelReport; const UserName: string; const UserUrl: string; const DataPath: string);
begin
//Set report variables, including an image.
ReportStart.SetValue('Date', Now);
ReportStart.SetValue('Name', UserName);
ReportStart.SetValue('TwoLines', 'First line' + #13#10 + 'Second Line');
ReportStart.SetValue('Empty', TReportValue.Empty);
ReportStart.SetValue('LinkPage', UserUrl);
ReportStart.SetValue('Img', TFile.ReadAllBytes(TPath.Combine(DataPath, 'img.png')));
end;
procedure TMainForm.NormalRun;
var
Report: TFlexCelReport;
FileExt: string;
begin
if not SaveDialog.Execute then exit;
Report := TFlexCelReport.Create(true);
try
Setup(Report, edName.Text, edURL.Text, GetDataPath);
//FlexCel doesn't do a 100% accurate conversion between xls and xlsx, it isn't designed for that
//So to create an xls file is it best to use an xls template, and for xlsx and xlsx template.
if TPath.GetExtension(SaveDialog.FileName) = '.xlsx' then FileExt := '.xlsx' else FileExt := '.xls';
Report.Run(
TPath.Combine(GetDataPath, 'Getting Started Reports.template' + FileExt),
SaveDialog.FileName);
finally
Report.Free;
end;
if MessageDlg('Do you want to open the generated file?', mtConfirmation, [mbYes, mbNo], 0) = mrYes then
begin
ShellExecute(0, 'open', PCHAR(SaveDialog.FileName), nil, nil, SW_SHOWNORMAL);
end;
end;
procedure TMainForm.AutoOpenRun;
var
Report: TFlexCelReport;
FilePath, FileName: string;
begin
Report := TFlexCelReport.Create(true);
try
Setup(Report, edName.Text, edURL.Text, GetDataPath);
FilePath := TPath.GetTempPath(); //GetTempFileName does not allow us to specify the "xltx" extension.
FileName := TPath.Combine(FilePath, TGuid.NewGuid.ToString + '.xltx'); //xltx is the extension for excel templates.
try
Report.Run(
TPath.Combine(GetDataPath, 'Getting Started Reports.template.xlsx'),
FileName);
ShellExecute(0, '', PCHAR(FileName), nil, nil, SW_SHOWNORMAL);
finally
{$if CompilerVersion < 28.0}
//For Delphi < XE7, we don't have TTask, so we use this other code instead.
//The code here is not perfect, since if you exit the app before the thread ends,
//Delphi will kill the thread and not delete the file.
//So for a real app, in Delphi < XE7, you would have to check OnTerminate in the app
//and delete pending files. For simplicity we won't do it here.
//The TTask version for Delphi >=XE7 will delete the file even if you exit the app.
//So when using Delphi>=XE7 you don't need to do anything.
TThread.CreateAnonymousThread(procedure begin
TThread.Sleep(30000); //wait for 30 secs to give Excel time to start.
TFile.Delete(FileName); //As it is an xltx file, we can delete it even when it is open on Excel.
end).Start;
{$ELSE}
//See https://doc.tmssoftware.com/flexcel/vcl/tips/automatically-open-generated-excel-files.html
TTask.Run(procedure begin
TThread.Sleep(30000); //wait for 30 secs to give Excel time to start.
TFile.Delete(FileName); //As it is an xltx file, we can delete it even when it is open on Excel.
end);
{$IFEND}
end;
finally
Report.Free;
end;
end;
end.