Dokumentacja HotXLS

Callback OnUserFunction / OnUserFunctionEx

Callback OnUserFunction pozwala kodowi aplikacji ewaluować niestandardowe lub nieobsługiwane funkcje arkusza podczas Calculate. Przypisz go do TXLSWorkbook lub TXLSXWorkbook przed wywołaniem Calculate
Użyj OnUserFunctionEx gdy callback potrzebuje bieżącego kontekstu formuły. Kontekst raportuje 1-based indeks arkusza oraz 1-based wiersz i kolumnę komórki formuły; wywołania Calculate na poziomie skoroszytu, które nie ewaluują komórki, raportują wiersz i kolumnę jako 0

Składnia

type
  TXLSUserArgKind = (akuValue, akuRangeRef, akuArray);

  TXLSUserFunctionContext = record
    SheetIndex: Integer;
    Row: Integer;
    Col: Integer;
    ArgCount: Integer;
    ArgKinds: array of TXLSUserArgKind;
  end;

  TXLSUserFunctionEvent = procedure(
    Sender: TObject;
    const FunctionName: WideString;
    const Args: Variant;
    var Value: Variant;
    var Handled: Boolean) of object;

  TXLSUserFunctionExEvent = procedure(
    Sender: TObject;
    const FunctionName: WideString;
    const Args: Variant;
    const Context: TXLSUserFunctionContext;
    var Value: Variant;
    var Handled: Boolean) of object;

property OnUserFunction: TXLSUserFunctionEvent;
property OnUserFunctionEx: TXLSUserFunctionExEvent;
property AllowUnsafeFormulaCallbacks: Boolean;

Argumenty

FunctionName Nazwa funkcji z tekstu formuły
Args Tablica Variant numerowana od zera, zawierająca obliczone wartości argumentów. Dla funkcji bez argumentów wartością jest Null
Context Dla OnUserFunctionEx raportuje SheetIndex, Row i Col, a do tego deskryptor argumentów: ArgCount odpowiada długości tablicy Args (0 dla wywołań bez argumentów), a ArgKinds klasyfikuje każdy argument jako akuValue (wartość literalna), akuRangeRef (odwołanie do komórki lub zakresu) albo akuArray (stała tablicowa). Wartości arkusza, wiersza i kolumny są numerowane od 1 dla formuł komórek; wiersz i kolumna mają wartość 0, gdy nie ewaluowana jest żadna komórka. Rodzaje są przechwytywane przed obliczeniem argumentów, więc procedura obsługi w stylu SUM czy MAP rozpozna, że wywołujący wpisał odwołanie, mimo że obliczony wpis Args niesie już samą wartość
Value Ustaw na wynik funkcji
Handled Ustaw na True, gdy callback zwraca wynik. Pozostaw False, aby zachować normalne zachowanie dla nieobsługiwanych funkcji

Zasady bezpieczeństwa callbacków

AllowUnsafeFormulaCallbacks ma domyślnie wartość False w skoroszytach Classic XLS i XLSX, więc samo zachowanie formuły nigdy nie uprawnia kodu aplikacji gospodarza do obsługi niebezpiecznych nazw funkcji zewnętrznych ani w stylu makro
Domyślna brama obejmuje DDE, CALL, REGISTER, REGISTER.ID, WEBSERVICE, RTD, SQL.REQUEST, EXEC, RUN, CREATE.OBJECT, APP.ACTIVATE, SEND.KEYS, OPEN, SAVE, SAVE.AS, FOPEN, FWRITE, FWRITELN, FCLOSE i FILE.DELETE po znormalizowaniu rozpoznanego prefiksu funkcji przyszłości
Odmowa następuje przed ewaluacją argumentów i zanim ruszą callbacki lokalne dla skoroszytu, całego procesu, resolvera lub zdarzeniowe; zwykłe nazwy własne i nieobsługiwane funkcji zachowują dotychczasowe zachowanie callbacków
Właściwość skoroszytu ustawiaj na True tylko wtedy, gdy każda formuła i zarejestrowana procedura obsługi są zaufane; ewaluacja kontekstowa może zamiast tego włączyć opcję na pojedyncze wywołanie przez TXLSFormulaEvaluationOptions.AllowUnsafeFormulaCallbacks, a jawnie podane False nadpisuje opt-in dla całego skoroszytu

Przykład

procedure TForm1.WorkbookUserFunction(Sender: TObject;
  const FunctionName: WideString; const Args: Variant;
  var Value: Variant; var Handled: Boolean);
begin
  if SameText(FunctionName, 'DOUBLEPLUS') then
  begin
    Value := Double(Args[0]) * 2 + Double(Args[1]);
    Handled := True;
  end;
end;

Workbook.OnUserFunction := WorkbookUserFunction;
Value := Workbook.Calculate('=DOUBLEPLUS(A1;5)');

Przykład z kontekstem

procedure TForm1.WorkbookUserFunctionEx(Sender: TObject;
  const FunctionName: WideString; const Args: Variant;
  const Context: TXLSUserFunctionContext;
  var Value: Variant; var Handled: Boolean);
begin
  if SameText(FunctionName, 'ROWBONUS') then
  begin
    Value := Double(Args[0]) + Context.Row + Context.Col;
    Handled := True;
  end;
end;

Workbook.OnUserFunctionEx := WorkbookUserFunctionEx;
Value := Worksheet.Calculate('=C2');

Odbieranie argumentów zakresu

Gdy argument ewaluowany jest do zakresu arkusza, wpis Args to Variant typu varUnknown trzymający ICalcRangeList; obliczona wartość każdej objętej komórki nie jest kopiowana do Variantu. Sprawdź to i wyciągnij listę przeciążeniem VarIsRange, które ją zwraca, a następnie odczytaj rozwiążone prostokąty przez TXLSCalcRange
type
  TXLSCalcRange = class
    property row1: Integer;   { 0-based, inclusive }
    property col1: Integer;   { 0-based, inclusive }
    property row2: Integer;
    property col2: Integer;
    property SheetIndex: Integer;
  end;

  ICalcRangeList = interface
    property Count: Integer;
    property Item[Index: Integer]: TXLSCalcRange;
  end;

function VarIsRange(Value: Variant): Boolean; overload;
function VarIsRange(Value: Variant; var Int: ICalcRangeList): Boolean; overload;
function RangeToVariant(Value: TXLSCalcRangeList): Variant;
    
Współrzędne trzymają się konwencji TXLSGetValue: numerowane od zera i inkluzywnie. Procedura obsługi, która sama chce zwrócić zakres, buduje TXLSCalcRangeList i opakowuje ją przez RangeToVariant przed przypisaniem Value; silnik zachowa wtedy odwołanie do zakresu dla formuł zależnych zamiast skalarnej migawki

Zobacz też