מאמר טכני

מנוע חישוב ופונקציות מותאמות ב-HotXLS עבור Delphi

HotXLS, ספריית גיליונות אלקטרוניים נייטיבית עבור Delphi ו-C++Builder, מכיל מנוע חישוב המאפשר לחשב נוסחאות ב-Delphi ללא Excel. מנוע זה פועל דרך Calculate ומתרחב ע"י callback events לפונקציות מותאמות אישית

Calculate: חישוב ביטוי כנגד החוברת הנטענת

Book.Calculate(Expression) מחשב ביטוי שרירותי כנגד החוברת הנטענת ומחזיר Variant. הפונקציה שימושית לבדיקה לפני מסירה — לדוגמה, חישוב סכום טווח לאחר מילוי שורות פירוט ובדיקתו מול הרשומה הראשית. בדוק תמיד VarIsNumeric על התוצאה לפני שימוש, מכיוון שמנוע עלול להחזיר שגיאה כ-Variant error

OnUserFunction ו-OnUserFunctionEx: callback לפונקציות מותאמות

HotXLS מאפשר להגדיר פונקציות מותאמות אישית ע"י חיבור ל-event OnUserFunction. ה-event מקבל שם פונקציה ומערך ארגומנטים, ומחזיר ערך. OnUserFunctionEx מרחיב זאת עם גישה לאובייקט הגיליון, מה שמאפשר לפונקציה לגשת לנתונים מהגיליון עצמו

Book.OnUserFunction := procedure(const FuncName: string;
  const Args: array of Variant; var Result: Variant)
begin
  if SameText(FuncName, 'MYRATE') then
    Result := Args[0] * 0.07
  else
    Result := Null; // signal unknown function
end;

פונקציות מותאמות אינן עוברות ל-Excel

פונקציה מותאמת שמחושבת ב-HotXLS נשמרת בגיליון כביטוי, ולא כפונקציה רשומה. כאשר Excel פותח את הקובץ, הוא מנסה לחשב את הנוסחה מחדש ומחזיר #NAME? מכיוון שאינו מכיר את שם הפונקציה. הפתרון: חשב את הערכים ב-Delphi ב-Calculate, ואז כתוב את הערכים המחושבים ישירות לתאים לפני שמירה

מצב חישוב בחזית XLS

בחזית XLS, IXLSWorkbook.CalculationMode ו-RecalcOnSave שולטים מתי Excel יחשב מחדש בפתיחה. ב-XLSX, Excel תמיד מחשב מחדש בפתיחה ללא תלות בהגדרות אלה

נוסחאות מערך

SetArrayFormula מגדיר נוסחת מערך על טווח תאים — שקול ל-Ctrl+Shift+Enter ב-Excel. כל הטווח מוחזק כיחידה אחת. בקריאה, תאים בתוך נוסחת מערך מדווחים על הנוסחה המקורית; שינוי תא בודד בתוך הטווח אינו נתמך

HotXLS קומפל לתוך אפליקציית Delphi או C++Builder ללא תלויות חיצוניות; מהדורות ורישוי זמינים בדף HotXLS Component

דוגמאות קוד נוספות

procedure TReportBuilder.HandleUserFunction(Sender: TObject;
  const FunctionName: WideString; const Args: Variant;
  var Value: Variant; var Handled: Boolean);
begin
  if SameText(FunctionName, 'DISCOUNT') then
  begin
    Value := Args[0] * 0.9;   // Args arrives as a Variant array
    Handled := True;
  end;
end;

// wiring and use
Book.OnUserFunction := HandleUserFunction;
Sheet.Cells[1, 1].Value := 200;
Sheet.Cells[1, 2].Formula := 'DISCOUNT(A1)';
Net := Book.Calculate('DISCOUNT(A1) + SUM(A1:A1)');
procedure TReportBuilder.HandleUserFunctionEx(Sender: TObject;
  const FunctionName: WideString; const Args: Variant;
  const Context: TXLSUserFunctionContext;
  var Value: Variant; var Handled: Boolean);
begin
  if SameText(FunctionName, 'REGIONRATE') then
  begin
    // the same formula yields a different rate on each regional sheet
    Value := RateForSheet(Context.SheetIndex) * Args[0];
    Handled := True;
  end;
end;
// one array formula spanning A2:A4
Sheet.RCRange[2, 1, 4, 1].SetArrayFormula('A1*{1;2;3}');

הערות בדיקה לסביבת ייצור

החלק הזה משלים את המאמר מנקודת מבט של שילוב אמיתי. לפני שמעתיקים את הדוגמה לקוד ייצור, חשוב להגדיר איזה קובץ נכנס לתהליך, איזה רכיב יוצר או קורא אותו, איזה פלט המשתמש אמור לקבל, ואילו ערכים חייבים להישמר ללא שינוי בין Delphi, C++Builder וכלים חיצוניים

בדקו את ההתנהגות עם מסמכים קטנים, קלט פגום ונתונים הדומים לסביבת הלקוח. בעיות של גופנים, עמודים, הצפנה, מטא-נתונים או קידוד מתגלות לרוב רק כאשר יש שמות ארוכים, תווים בינלאומיים, אובייקטים מקוננים וערכים ריקים באותו תרחיש

כאשר התהליך רץ כשירות או כמשימת אצווה, הוסיפו בדיקות לשחרור משאבים, שימוש חוזר ברכיבים, טיפול בחריגות ורישום סיבת הכשל. התוצאה צריכה להיות ברורה: הקובץ נוצר, הקובץ נדחה עם סיבה מובנת, או שניתן להריץ את הפעולה מחדש בבטחה