Overview
Here we will create a small fully functional Minesweeper in Excel, and
without any macros.
Most of the time, you will just want to write the formulas directly on
the report, and let FlexCel change them when inserting rows. But
sometimes, you might want to create a formula using flexcel tags. You
can do it by using <#formula> and <#ref> tags, but don't do it
unless you really need to.
Concepts
How to replace <#tags> inside a formula.
When you use the <#Formula> tag, formulas are just strings on the
template. So we could define inside a cell: "<#formula>=b3",
but then if we want to change the template, and insert a row below
row 1, the string will still be "<#formula>=b3". That is why
we need to use a <#Ref> tag. For example
<#formula>=<#ref(0;-1)> on cell c3 will point to b3, but if
you insert a row below row 1, it will point to b4 as it should be.
There is never a real need to use the <#formula> tag, you can
always workaround it. On this particular case we used it so we
could change the font, (since it is not possible to change the
font on a conditional format), but we could have also used the
approach we used on the Expression Parameters demo. In fact,
it might have been better that way (by adding the <#format
range> tag in the data). But this demo is to show how to use this
feature, it is up to you to decide if it is worth or not.
How to use conditional formatting, locked cells and data validation
to make a nice application.
Files
DataModel.pas
unit DataModel;
interface
type
//Delphi doesn't provide RTTI in record properties:
//http://qc.embarcadero.com/wc/qcmain.aspx?d=78110
//
//As we want to use records instead of classes for simplicity,
//we can't use properties. We'll use public fields instead.
TMine = record
public
Value: integer;
end;
TColOfMines = record
public
datacol: TArray<TMine>;
procedure CreateCol(const count: integer);
end;
TMines = TArray<TColOfMines>;
implementation
{ TColOfMines }
procedure TColOfMines.CreateCol(const count: integer);
begin
SetLength(DataCol, count);
end;
end.
UMainForm.pas
unit UMainForm;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics,
FlexCel.VCLSupport, FlexCel.Core, FlexCel.XlsAdapter, FlexCel.Report, FlexCel.Render,
{$if CompilerVersion >= 23.0} System.UITypes, {$IFEND}
ShellApi, Generics.Collections,
Controls, Forms, Dialogs, StdCtrls;
type
TMainForm = class(TForm)
btnCancel: TButton;
btnGo: TButton;
SaveDialog: TSaveDialog;
Label1: TLabel;
procedure btnCancelClick(Sender: TObject);
procedure btnGoClick(Sender: TObject);
private
procedure RunReport;
function GetDataPath: string;
procedure SetupMines(const MinesReport: TFlexCelReport);
procedure AfterGenerateWorkbook(const sender: TObject;
const e: TGenerateEventArgs);
{ Private declarations }
public
{ Public declarations }
end;
var
MainForm: TMainForm;
implementation
uses IOUtils, DataModel;
{$R *.dfm}
procedure TMainForm.btnCancelClick(Sender: TObject);
begin
Close;
end;
procedure TMainForm.btnGoClick(Sender: TObject);
begin
RunReport;
end;
function TMainForm.GetDataPath: string;
begin
Result := TPath.Combine(TPath.GetDirectoryName(ParamStr(0)), '..\..');
end;
procedure TMainForm.SetupMines(const MinesReport: TFlexCelReport);
var
Mines: TMines;
i: Integer;
k: Integer;
M: TDictionary<integer, boolean>;
begin
Randomize;
SetLength(Mines, 9);
M := TDictionary<integer, boolean>.Create;
try
//We'll create 10 mines
while M.Count < 10 do
begin
M.AddOrSetValue(Random(9 * 9 - 1), true);
end;
for i := 0 to High(Mines) do
begin
Mines[i].CreateCol(9);
for k := 0 to High(Mines[i].datacol) do if M.ContainsKey(i * 9 + k) then Mines[i].datacol[k].Value := 1;
end;
finally
M.Free;
end;
MinesReport.ClearTables;
MinesReport.AddTable<TColOfMines>('datarow', Mines);
end;
procedure TMainForm.AfterGenerateWorkbook(const sender: TObject; const e: TGenerateEventArgs);
var
r, c: integer;
begin
//do some "pretty" up for the final user.
//we could do this directly on the template, but doing it here allows us to keep the template unprotected and easier to modify.
e.DataFile.ActiveSheet := 2;
e.DataFile.SheetVisible := TXlsSheetVisible.Hidden;
e.DataFile.ActiveSheet := 1;
e.DataFile.Protection.SetSheetProtection('', TSheetProtectionOptions.Create(true));
for r := 20 to TFlxConsts.Max_Rows97_2003 do e.DataFile.SetRowHidden(r, true);
for c := 12 to TFlxConsts.Max_Columns97_2003 do e.DataFile.SetColHidden(c, true);
end;
procedure TMainForm.RunReport;
var
Report: TFlexCelReport;
begin
if not SaveDialog.Execute then exit;
Report := TFlexCelReport.Create(true);
try
SetupMines(Report);
Report.AfterGenerateWorkbook := AfterGenerateWorkbook;
Report.Run(
TPath.Combine(GetDataPath, 'Manual Formulas.template.xls'),
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;
end.