Search Results for

    Show / Hide Table of Contents

    User defined functions (Delphi)

    Note

    This demo is available in your FlexCel installation at <FlexCel Install Folder>\Demo\Delphi\Modules\20.Reports\95.User Defined Functions and also at https:​//​github.​com/​tmssoftware/​TMS-​FlexCel.​VCL-​demos/​tree/​master/​Delphi/​Modules/​20.​Reports/​95.​User Defined Functions

    Overview

    Here we will see how to define a user function to calculate the number of orders by employee and shipper. This example is similar to the Pivot Tables example, but using only static normal cells.

    Concepts

    • How to define user functions for non-standard needs. By the way: Try to define only as little user functions as you really need. User functions are on code, and a change on them means that you have to recompile the code.

    • We defined two ranges here, one horizontal and one vertical. The user defined function here will return a value for a couple of those values, allowing us to create a "static" pivot table.

    Files

    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,
      Controls, Forms, Dialogs, StdCtrls, ExtCtrls;
    
    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;
        { Private declarations }
      public
        { Public declarations }
      end;
    
    var
      MainForm: TMainForm;
    
    implementation
    uses IOUtils, DemoOrders, UOrdersImp;
    
    {$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.RunReport;
    var
      Report: TFlexCelReport;
    begin
      if not SaveDialog.Execute then exit;
    
      Report := TFlexCelReport.Create(true);
      try
        Report.AddTable('Employees', DemoTables.Employees);
        Report.AddTable('Shippers', DemoTables.Shippers);
        Report.SetUserFunction('Orders', TOrdersImp.Create(DemoTables.Orders));
        Report.SetValue('Date', Now);
        Report.Run(
          TPath.Combine(GetDataPath, 'User Defined Functions.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.
    

    UOrdersImp.pas

    unit UOrdersImp;
    
    interface
    uses DB, FlexCel.Core, FlexCel.Report, Generics.Collections;
    
    type
      TOrdersImp = class(TFlexCelUserFunction)
      private
        Orders: TDictionary<Int64, integer>;
        function GetKey(const Employee, Shipper: integer): Int64;
      public
        constructor Create(const aOrders: TDataSet);
        destructor Destroy; override;
        function Evaluate(const parameters: TFormulaValueArray): TReportValue; override;
      end;
    
    implementation
    uses SysUtils;
    
    { TOrdersImp }
    
    constructor TOrdersImp.Create(const aOrders: TDataSet);
    var
      key: Int64;
      count: integer;
    begin
      Orders := TDictionary<Int64, integer>.Create;
    
      aOrders.Open;
      try
        aOrders.First;
        while not aOrders.Eof do
        begin
          key := GetKey(aOrders['EmployeeId'], aOrders['ShipVia']);
          if Orders.TryGetValue(Key, count) then
          begin
            Orders[Key] := count + 1;
          end
          else
          begin
            Orders.Add(Key, 1);
          end;
    
          aOrders.Next;
        end;
      finally
        aOrders.Close;
      end;
    end;
    
    destructor TOrdersImp.Destroy;
    begin
      Orders.Free;
      inherited;
    end;
    
    function TOrdersImp.Evaluate(
      const parameters: TFormulaValueArray): TReportValue;
    var
      Employee, ShipVia: Integer;
      Ri: Integer;
      i: Integer;
    begin
      if Length(Parameters) <> 2 then raise Exception.Create('Bad parameter count in call to Orders() user-defined function.');
      for i := 0 to High(Parameters) do
      begin
        if not Parameters[i].IsNumber then exit(TReportValue.Empty); //might be null
      end;
    
      Employee := Round(Parameters[0].AsNumber);
      ShipVia := Round(Parameters[1].AsNumber);
      if not Orders.TryGetValue(GetKey(Employee, ShipVia), Ri) then exit(TReportValue.Empty);
      Result := Ri;
    end;
    
    function TOrdersImp.GetKey(const Employee, Shipper: integer): Int64;
    begin
      //For simplicity, we'll just keep the key in an int64 with the employee in the high int32 and the shipper in the low int32
      //To do this better, we should create a record with shipper and employee and define a GetHashCode.
      Result := (Int64(Employee) shl 32) or Int64(Shipper);
    end;
    
    end.
    
    In This Article
    Back to top FlexCel Studio for VCL and FireMonkey v7.24
    © 2002 - 2025 tmssoftware.com