Table of Contents

FlexCalc (FireMonkey Mobile)

Note

This demo is available in your FlexCel installation at <FlexCel Install Folder>\Demo\FireMonkey Mobile\Modules\40.FlexCalc and also at https:​//​github.​com/​tmssoftware/​TMS-​FlexCel.​VCL-​demos/​tree/​master/​Fire​Monkey Mobile/​Modules/​40.​FlexCalc

Overview

This example shows how to use the FlexCel calculating engine as a calculator for the device.

Concepts

  • Under the hood, every textbox in the app is mapped to a cell in a spreadsheet (A1, A2, A3...), and whenever a textbox changes, the sheet is recalculated and the results of the new calculations are shown at the column on the right. You can use the full range of Excel functions in this demo, and reference cells in the usual way (for example, you can write "=A2 + 1" in the textbox for A3)

    As in Excel, any text that starts with "=" will be considered a formula.

    The backing spreadsheet will be saved when you exit the app and loaded when open it, and this will allow to persist the formulas between sessions.

Files

UFlexCalc.pas

unit UFlexCalc;

interface

uses
  System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
  FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs, FlexCel.FMXSupport, FlexCel.Core, FlexCel.XlsAdapter,
  FMX.StdCtrls, FMX.Edit;

type
  TFFlexCalc = class(TForm)
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure FormResize(Sender: TObject);
  private
    const
      RowHeight = 20;
      Margin = 10;
      HeadingW = 40;
      ResultsW = 200;
    var
    xls: TExcelFile;
    function ConfigFile: string;
    procedure EditChange(Sender: TObject);
    function GetCellOrFormula(const row: integer): string;
    procedure SetCellSize(Top: single; CellValue: TEdit);
  public
  end;

var
  FFlexCalc: TFFlexCalc;

implementation
uses IOUtils;

{$R *.fmx}

function TFFlexCalc.ConfigFile: string;
begin
  Result := TPath.Combine(TPath.GetDocumentsPath, 'config.xlsx');
end;

procedure TFFlexCalc.EditChange(Sender: TObject);
var
  z, k: integer;
begin
  z := (Sender as TEdit).Tag;
  xls.SetCellFromString(z + 1, 1, (Sender as TEdit).Text);
  xls.Recalc;
  xls.Save(ConfigFile);

  for k := 0 to ChildrenCount - 1 do
  begin
    if (Children[k] is TLabel) and (Children[k].Tag >= 0) then
    begin
      (Children[k] as TLabel).Text := xls.GetStringFromCell(Children[k].Tag + 1, 1);
    end;
  end;

end;

procedure TFFlexCalc.FormCreate(Sender: TObject);
const
  Predefined: array[0..10] of string =
            ('5', '=A1 * 3 + 7', '=Sum(A1, A2)*9', '=Sin(a1) + cos(a2)',
            '=Average(a1:a4)', '', '', '', '', '', '');

var
  Restoring: boolean;
  k: integer;
  ColHeading: TLabel;
  CellValue: TEdit;
  Results: TLabel;
begin
  Restoring := false;
  xls := TXlsFile.Create(true);
  if TFile.Exists(ConfigFile) then
  begin
    try
      xls.Open(ConfigFile);
      Restoring := true;
    except
      //if the file is corrupt, we'll just ignore it.
      //Restoring will be false, and we will create a new file.
    end;
  end;

  if (not Restoring) then
  begin
    xls.NewFile(1, TExcelFileFormat.v2019);

    for k := 0 to Length(Predefined) - 1 do
    begin
      xls.SetCellFromString(k + 1, 1, Predefined [k]); //Initialize the grid with something so users know what they have to do.
    end;
  end;

  xls.Recalc();

  for k := 0 to xls.RowCount - 1 do
  begin
    ColHeading := TLabel.Create(self);
    ColHeading.SetBounds(Margin, Margin + k * (RowHeight + 2 * Margin), HeadingW, RowHeight + Margin);
    ColHeading.Text := TCellAddress.Create(k + 1, 1).CellRef;
    ColHeading.Tag := -1;
    ColHeading.Parent := self;

    CellValue := TEdit.Create(self);
    SetCellSize(Margin + k * (RowHeight + 2 * Margin), CellValue);
    CellValue.Text := GetCellOrFormula(k + 1);
    CellValue.Tag := k;
    CellValue.OnChangeTracking := EditChange;
    CellValue.Parent := self;

    Results := TLabel.Create(self);
    Results.SetBounds(CellValue.Position.X + CellValue.Width + Margin, Margin + k * (RowHeight + 2 * Margin),
          ResultsW, RowHeight + Margin);
    Results.Text := xls.GetStringFromCell(k + 1, 1);
    Results.Tag := k;
    Results.Parent := self;
  end;
end;

procedure TFFlexCalc.FormDestroy(Sender: TObject);
begin
  if xls <> nil then xls.Save(ConfigFile);
  FreeAndNil(xls);
end;

procedure TFFlexCalc.FormResize(Sender: TObject);
var
  i: integer;
begin
 for i := 0 to ChildrenCount - 1 do
 begin
   if Children[i] is TEdit then
   begin
     SetCellSize((Children[i] as TEdit).Position.Y, Children[i] as TEdit);
   end
   else if (Children[i] is TLabel) and ((Children[i] as TLabel).Tag >= 0) then
   begin
     (Children[i] as TLabel).Position.X := ClientWidth - (Children[i] as TLabel).Width - Margin;
   end;

 end;
end;

function TFFlexCalc.GetCellOrFormula(const row: integer): string;
var
  cell: TCellValue;
begin
  cell := xls.GetCellValue(row, 1);
  if (cell.IsEmpty) then exit ('');
  if (cell.IsFormula) then exit (cell.AsFormula.Text);
  exit(cell.ToString);
end;

procedure TFFlexCalc.SetCellSize(Top: single; CellValue: TEdit);
begin
  CellValue.SetBounds(HeadingW + 2 * Margin, Top, ClientWidth - HeadingW - ResultsW - 4 * Margin, RowHeight);
end;
end.