מאמר טכני

אימות נתונים, AutoFilter וטבלאות ב-HotXLS עבור Delphi

שלוש תכונות ב-HotXLS חולקות גיליון עבודה אך פועלות על אובייקטים שונים לגמרי, והצרות מתחילות כשמניחים שהן עושות דברים דומים. אימות נתונים מצמיד כלל לטווח המגביל את מה שמשתמש רשאי להקליד בו. AutoFilter מצמיד הגדרת קריטריונים שמורה לאזור ומשנה אילו שורות מציג מציג. טבלה עוטפת טווח במבנה בעל שם, טיפוסים ועיצוב מפוספס. אחת מגבילה קלט, אחת מתעדת תצוגה ואחת כופה סכימה. אף אחת מהן אינה מזיזה בעצמה אפילו ערך תא אחד, ובייחוד AutoFilter מטעה אנשים, מפני שהמילה מרמזת על פעולה בעוד שהוא שומר הגדרה בלבד. הידיעה באיזה אובייקט נוגעת כל קריאה ומתי ההשפעה מתממשת בפועל היא שמבדילה חוברת המתנהגת ב-Excel כפי שהתנהגה בבדיקות שלך מחוברת הסוטה בשקט

תרשים של שלוש יכולות גליון עבודה ב-HotXLS ב-Delphi שבהן אימות נתונים מגביל קלט, AutoFilter מאחסן הגדרת תצוגה, וטבלה כופה סכימה
אימות נתונים, AutoFilter וטבלאות כולן נצמדות לאותו טווח גיליון ב-HotXLS, אך כל אחת מתגשמת ברגע אחר — הקלדה, פתיחת קובץ ושמירה

AutoFilter שומר הגדרה, הוא אינו גוזם שורות

AutoFilter בקובץ שמור הוא רשומת קריטריונים. הסתרת השורות מתרחשת מאוחר יותר, כאשר Excel פותח את החוברת ומעריך את הקריטריונים מול הנתונים. HotXLS כותב את הרשומה הזאת נאמנה ואינו גוזם דבר: כל שורה שסוננה עדיין נוכחת פיזית בקובץ. צינור המפעיל מסנן כדי להשמיט הזמנות שנדחו ואז קורא את החוברת בחזרה יראה את כולן, כולל הדחויות, והקוד יהיה נכון לפי ה-API אך שגוי לפי המודל המנטלי של הכותב. בגיליון XLSX, SetAutoFilter מצהיר על האזור המסונן ו-AddAutoFilterColumn מצמיד קריטריונים לעמודה אחת בו. כאשר קוד שרת זקוק לתוצאה בפועל, לספירת שורות בסיכום או להעברת שורות תואמות בלבד, הספרייה מעריכה עבורך את הקריטריונים במקום להעמיד פנים שהקובץ השתנה:

var
  Book: TXLSXWorkbook;
  Sheet: TXLSXWorksheet;
  R, Visible: Integer;
begin
  Book := TXLSXWorkbook.Create;
  try
    Book.Open('orders.xlsx');
    Sheet := Book.Sheets[0];

    Sheet.SetAutoFilter('A1:E500');
    // מזהה עמודה 3 = העמודה הרביעית בתוך טווח המסנן (היסט מבוסס-0)
    Sheet.AddAutoFilterColumn(3, xlsxAfOpGreaterOrEqual, '1000');

    Visible := 0;
    for R := 2 to 500 do
      if Sheet.AutoFilterRowVisible(R) then
        Inc(Visible);
    // Visible תואם כעת למה ש-Excel יציג לאחר פתיחת הקובץ

    Book.SaveAs('orders-filtered.xlsx');
  finally
    Book.Free;
  end;
end;

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

מזהה העמודה הוא היסט, לא מספר עמודה

ההערה בקטע שלמעלה מסמנת את המלכודת הגוזלת את זמן הניפוי הרב ביותר ב-API זה. AddAutoFilterColumn מזהה את יעדו לפי המיקום המבוסס-0 בתוך טווח המסנן, ולא לפי עמודת הגיליון. עבור מסנן על A1:E500, שתי שיטות המספור נבדלות במקרה באחד, בדיוק סוג ההחמצה הכמעטית ששורד בדיקה מהירה ונשבר כשעמית מסנן עמודה אחרת. עבור מסנן המתחיל בעמודה C, מזהה 0 פירושו עמודה C, וחוסר ההתאמה מתבהר מהר. כאשר טווח המסנן מחושב בזמן ריצה, גזור את מזהה העמודה מאותו משתנה שבנה את מחרוזת הטווח, לעולם לא מקבוע עמודת גיליון. כל עמודה מקבלת תנאי שני באמצעות גרסת העומס המקבלת שני אופרטורים, שני קריטריונים ומחבר של וגם/או, המשקף את תיבת הדו-שיח של המסנן המותאם אישית ב-Excel. חזית XLS מכסה אותו תחום באמצעות SetAutoFilter יחד עם ApplyAutoFilter, שהקריטריונים ופרמטרי האופרטור שלהם פועלים לפי מוסכמות COM הוותיקות וממספרים את השדה מ-1. החלפת חזיתות פירושה החלפת בסיסי אינדקסים, ולכן אתר הקריאה ראוי להערה המציינת איזה מהם בשימוש

תרשים המציג AutoFilter של HotXLS מאחסן כל שורה בקובץ ה-Excel הנשמר בעוד ה-API לתצוגה מקדימה ב-Delphi מעריך אילו שורות Excel יציג, עם היסט מזהי העמודות המבוסס-אפס
הקובץ הנשמר מחזיק כל שורה ורק רושם את הקריטריונים, בזמן ש-Excel מסתיר שורות אחרי הערכתן — ו-AddAutoFilterColumn פונה אל עמודות לפי היסט המתחיל באפס בתוך הטווח

כללי אימות הם החוזה שהמשתמשים שלך עורכים תחתיו

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

var
  Idx: Integer;
begin
  Idx := Sheet.AddListValidation('C2:C500', 'New,Approved,Blocked');
  Sheet.DataValidations[Idx].SetPrompt('Status',
    'Pick one of the listed states');
  Sheet.DataValidations[Idx].SetError('Invalid status',
    'Type or paste only listed values', xlsxDvErrStop);
  Sheet.DataValidations[Idx].AllowBlank := False;

  // כמויות: מספרים שלמים, אפס ומעלה
  Sheet.AddWholeNumberValidation('D2:D500', xlsxDvOpGreaterOrEqual, '0');
end;

מעבר לרשימות ולמספרים שלמים, אותה משפחה מכסה עשרוניים, תאריכים, שעות, אורך טקסט ונוסחאות חופשיות באמצעות AddCustomValidation, והכללי AddDataValidation חושף את מטריצת הטיפוסים והאופרטורים המלאה עבור בוני כללים המונעים מתצורה. סגנון השגיאה חשוב יותר משמו. xlsxDvErrStop דוחה קלט שגוי על הסף; סגנונות האזהרה והמידע מאפשרים לערך לעבור אחרי לחיצה יחידה. בחר לפי עמודה בהתבסס על השאלה אם הקוד הקורא את החוברת בחזרה יכול לסבול ערך מחוץ לכלל. שני גבולות צריכים להופיע בטקסט ההנחיה או ב-README המסופק עם הקובץ. אימות ב-Excel מגן על הקלדה, אך הדבקת בלוק על פני טווח מאומת חומקת מן הכלל, לכן כל קוד הקורא את הנתונים בחזרה חייב לאמת שוב ולא לבטוח בתאים. וכלל מכסה את הטווח המילולי שנמסר לו, כלומר הצמדת אימות לפני שידוע מספר השורות הסופי משאירה את הזנב שנוסף ללא הגנה. כתוב תחילה את הנתונים, ואז התאם את גודל הכללים להיקף בפועל

החזית הוותיקה מציעה אותן משפחות כללים בהבדל נוחות אחד. היוצרים בצד XLS, כלומר AddWholeNumberValidation, AddDecimalValidation, AddDateValidation, AddTimeValidation, AddTextLengthValidation ו-AddCustomValidation, מחזירים ישירות את אובייקט TDataValidation במקום אינדקס, ולכן תצורת ההנחיה והשגיאה ממשיכה מן ההפניה המוחזרת במקום מחיפוש. מניית האופרטורים (xlsDvBetween, xlsDvGreaterThan והשאר) משקפת את קבוצת XLSX, כך שקוד בניית כללים עובר בין חזיתות פרט להבדל בסגנון ההחזרה. לטקסט ההנחיה עצמו מגיעה מחשבה רבה לא פחות מהכלל. רשימה נפתחת הדוחה קלט עם תיבת שגיאה ריקה מלמדת משתמשים לשלוח דואר ל-IT; אחת המציינת את המצבים החוקיים מלמדת אותם לתקן את התא ולהמשיך

היפוך קוטביות אחד שהספרייה סופגת עבורך

מי שקרא ידנית XML של אימות OOXML פגש את המאפיין ההפוך showDropDown: ב-ISO/IEC 29500 ערך true פירושו "הסתר את חץ הרשימה הנפתחת", ההפך ממה שמשתמע מן השם. HotXLS הופכת זאת פנימית, לכן המאפיין ShowDropDown בכלל אימות פירושו את מה שהוא אומר, כאשר true מציג את הרשימה הנפתחת. הדרך היחידה להיכוות היא לערבב רמות אמת, להגדיר את המאפיין מהקוד בזמן שעמית בודק את ה-XML השמור ו"מתקן" את המאפיין הנראה לו הפוך. החלט אם המאפיין או ה-XML הגולמי סמכותיים עבור כלי סקירה, ותעד את ההיפוך במקום שבו ההחלטה הזו חיה

טבלאות מעניקות לטווח סכימה ושם

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

תרשים של טבלת גליון עבודה ב-HotXLS ב-Delphi עם עמודות מטופלסות, הפניות מובנות, שמות ייחודיים לחוברת, ומלכודת הוספת שורת הסכומים
טבלת HotXLS עוטפת את הטווח שלה בשם, עמודות מטופוס וסגנון מפוספס, בזמן ששורת הסיכום יושבת בדיוק מתחת לנתונים שם שרשור שורה-אחרונה נאיבי נוחת
var
  Cols: TStringList;
begin
  Cols := TStringList.Create;
  try
    Cols.CommaText := 'OrderId,Customer,Status,Amount,Owner';
    Sheet.AddTable('Orders', 'A1:E500', Cols);
  finally
    Cols.Free;
  end;
end;

בצד XLSX, אובייקט הטבלה המתקבל חושף את StyleName (משפחת TableStyleMedium2 המובנית ואחיותיה), מתגי פסים ודגל שורת סיכומים, לכן החלת סגנון ארגוני היא הקצאת מאפיין ולא מעבר עיצוב ידני. בקובצי .xls מדור קודם אותה קריאה כותבת רשומות טבלת BIFF8, והחזית מציעה גם את AddPivotTable לתצוגות סיכום הבנויות משדות שורה, עמודה ונתונים, תזכורת לכך ש"טבלאות" בפורמט הישן מגיעות רחוק יותר מ-ListObject של OOXML. תן שמות לטבלאות כפי שאתה נותן שמות לתצוגות מסד נתונים. קוד המשך הקורא Orders[Amount] באמצעות הפניה מובנית שורד את סידור העמודות מחדש השובר קוד מיקומי

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

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

אימות, מסננים וטבלאות הם ההבדל בין מסירת רשת ערכים לבין מסירת יישום קטן. ההפניה המלאה לכללים, מסננים וטבלאות נמצאת בדף המוצר של HotXLS Delphi Component