לשלוש קבוצות של הגדרות גיליון עבודה אין דבר עם ערכי התאים וכל הקשר לאופן שבו הקובץ מתנהג ברגע שהוא עוזב את הקוד שלכם. הגנת גיליון מכריעה אילו תאים משתמש יכול לערוך לאחר שתמסרו את חוברת העבודה. הגדרת עמוד מקבעת כיוון, גודל נייר ושוליים. הגדרות ההדפסה (שורות כותרת חוזרות, התאמת קנה מידה ומעברי עמוד ידניים) שולטות באופן שבו רשת באורך שרירותי נוחתת על נייר. אף אחת מהשלוש אינה מופיעה כשאתם מתבוננים בנתונים במציג, ושלושתן נשברות בשקט בשטח כשהן שגויות. HotXLS, ספריית גיליונות ילידית ל-Delphi ול-C++Builder, חושפת את המשטח השלם עבור .xls ו-.xlsx, מה שאומר שהיא גם משחזרת כל כלל Excel נוגד-אינטואיציה האפוי בתוך אותו משטח
הראשון מהכללים האלה מכשיל כמעט כל אחד בפעם הראשונה שהוא מגן על גיליון מחולל. קראו ל-Protect ולפתע איש אינו יכול להקליד לתוך אף תא, כולל עמודות הקלט שבנו את חוברת העבודה סביבן. שום דבר בקוד שלכם לא נגע בעמודות האלו, וזו בדיוק הסיבה שזה קורה
כל תא נולד נעול
ECMA-376 מגדיר את locked כחלק מרשומת העיצוב של תא, ולא כמאפיין של ההגנה עצמה, וברירת המחדל שלו היא true. הגנת גיליון היא רק המתג שהופך את הדגל לבר-אכיפה. כך שכל הרשת נושאת דגל נעילה מרגע שהיא קיימת, רדומה, והקריאה ל-Protect מפעילה את כולם בבת אחת. התיקון הוא לקבוע את הסדר בכוונה: בנו את הפריסה, בטלו במפורש את נעילת הטווחים שמשתמשים חייבים לערוך, והגנו אחרון
Book := TXLSXWorkbook.Create;
try
Sheet := Book.Sheets.Add('Timesheet');
// ... שורת כותרת, עמודת שמות ונוסחאות תעריף נכתבות כאן ...
Sheet.Range['B2:B50'].SetLocked(False); // הצוות מקליד שעות כאן
Sheet.Range['F2:F50'].SetFormulaHidden(True); // שמרו את חישוב התעריף פרטי
Sheet.Protect('review-2026'); // כעת דגלי הנעילה נושכים
Book.SaveAs('timesheet.xlsx');
finally
Book.Free;
end;
SetFormulaHidden עושה משהו נפרד וקל לפספס: בעוד ההגנה פעילה, התא עדיין מציג את ערכו המחושב, אך שורת הנוסחאות אינה מציגה דבר. זה חשוב כשנוסחה מטמיעה תעריפי חיוב, מרווחים או משקלי ניקוד שהייתם מעדיפים לא למסור לכל נמען שלוחץ על סיכום. בחזית ה-XLS אותה כוונה מובעת לכל טווח דרך IXLSRange.Locked ו-FormulaHidden. גיליון העבודה שם נושא גם חמישה-עשר דגלי Allow* (AllowSort, AllowAutoFilter, AllowFormatCells והשאר), כך שגיליון מוגן עדיין יכול להיות ממוין ומסונן ולא קפוא לכלל מוצג חתום
על מה סיסמת ההגנה באמת מגנה
שני הפורמטים מאחסנים את סיסמת הגנת הגיליון וחוברת העבודה כגיבוב מורשתי בן 4 ספרות הקס. שישה-עשר ביטים אומרים שאינספור מחרוזות מתנגשות עם כל סיסמה נתונה, וכלי הסרה הם חיפוש אחד משם. התייחסו להגנה כחגורת בטיחות מפני עריכות מקריות, לא כבקרת גישה. זהו הכלי הנכון לעצירת מבקרים מלהקליד מעל עמודת הנוסחה והכלי השגוי לכל דבר שכרוך במילה חסוי
רמה אחת מעלה, ProtectWorkbook בחזית ה-XLSX נועל את מבנה חוברת העבודה, מה שמונע הוספה, שינוי שם, מחיקה או סידור מחדש של גיליונות. הגדירו זאת בכל פעם שרשימת הגיליונות היא בעצמה חוזה עם מנתח במורד הזרם שמאנדקס גיליונות לפי שם או מיקום. גיליון ששמו שונה שובר את הייבוא בצד השני באותה ודאות שעמודה שנמחקה עושה. חזית ה-XLS משקפת את הריבוד עם TXLSWorkbook.Protect ברמת חוברת העבודה וקריאות Protect לכל גיליון, בתוספת מאפיין isProtected עבור קוד שצריך לבחון קובץ מורש לפני שהוא משנה דבר
כשהדרישה היא סודיות אמיתית, המנגנון משתנה לחלוטין. SaveAsEncrypted מפיק חבילה מוצפנת-AES תחת סכמת ההצפנה הסטנדרטית של ECMA-376, מכוסה לעומק במדריך פלט XLSX מוגן-AES, וחזית ה-XLS המורשתית כותבת וקוראת קובצי .xls מוצפני-RC4 דרך EncryptionPassword והעמסת הסיסמה של Open. ההבדל אינו אקדמי. גיליון מוגן נוסע בטקסט גלוי, כך שכל כלי zip יכול לקרוא את ערכי התאים שלו, בעוד חבילה מוצפנת אינה ניתנת לקריאה ללא הסיסמה. שורת ביקורת שאומרת "קובץ השכר חייב להיות מוגן" כמעט תמיד מתכוונת להצפנה, יהא אוצר המילים שבו היא משתמשת אשר יהא
הגדרת עמוד היא חלק מחוזה המסמך
התנהגות ההדפסה בלתי-נראית על המסך, וזו הסיבה שהיא נשלחת שבורה כה לעתים קרובות. ברגע שלקוח מדפיס את חוברת העבודה, או מייצא אותה ל-PDF עבור מבקר, שוליים, התאמת קנה מידה וכותרות חוזרות הופכים לדרישות פונקציונליות שאיש לא בדק. בחזית ה-XLSX הגדרות אלו תלויות ישירות בגיליון העבודה:
Sheet.PageLandscape := True;
Sheet.PaperSize := xlsxPaperA4;
Sheet.SetPageMargins(0.5, 0.5, 0.75, 0.75, 0.3, 0.3);
Sheet.CenterHeader := 'Monthly Timesheet';
Sheet.RightFooter := 'Page &P of &N';
Sheet.PrintArea := '$A$1:$F$60'; // הפניה חשופה: ללא שם גיליון כאן
Sheet.PrintTitleRows := '$1:$1'; // שורת הכותרת חוזרת בכל עמוד
Sheet.FitToWidth := 1;
Sheet.FitToHeight := 0; // גדלו כלפי מטה ככל שהנתונים גדלים
Sheet.PrintGridlines := False;
שתיים מהשורות האלו מחביאות מלכודות. מחרוזות הכותרת העליונה והתחתונה משתמשות בקודי העיצוב של Excel: &P לעמוד הנוכחי, &N לספירה הכוללת, עם &L, &C ו-&R כדי לפנות לשלושת המקטעים במפורש. המלכודת האחרת היא PrintArea, שנוטל הפניית תא חשופה בכוונה. HotXLS מאחסן אותה לא-מוכשרת ומקדים את שם הגיליון כשהוא כותב את הקובץ, ולכן העברת 'Timesheet!$A$1:$F$60' בעצמכם מפיקה הפניה כפולת-הכשרה ופגומה. אותה זהירות חלה שכבה אחת למטה: אזורי הדפסה וכותרות הדפסה נשמרים כשמות המוגדרים המובנים _xlnm.Print_Area ו-_xlnm.Print_Titles, ולכן לעולם אל תוסיפו רשומות _xlnm.* דרך DefinedNames ביד, אחרת שני המנגנונים יילחמו על אותה משבצת
התאמת קנה מידה ששורדת נפחי נתוני ייצור
הצירוף FitToWidth := 1 עם FitToHeight := 0 נקרא כ"התאם תמיד את העמודות על פני עמוד אחד, ואז קח כמה עמודים כלפי מטה שהנתונים זקוקים להם", וזו ברירת המחדל הנכונה לכל דוח שספירת השורות שלו משתנה. המלכודת היא כיוונון אחוז קבוע או זוג התאם-לעמוד מול קובץ בדיקה בן שלושים שורות: הזינו לאותן הגדרות שש מאות שורות ייצור והפלט או מתפוצץ לעשרות עמודים גזורים או מתכווץ מתחת לקריאוּת. התאימו את הרוחב, תנו לאורך לגדול, וחזרו על שורת הכותרת דרך PrintTitleRows כך שעמוד שבעה-עשר עדיין קריא בפני עצמו
מעברים ידניים עוקבים אחר אותה משמעת התחדשות כמו כל דבר אחר בחוברת עבודה מחוללת. AddRowBreak(BeforeRow) מתחיל עמוד חדש לפני גבול מקטע, אך כשהמחולל רץ מחדש והשורות זזות, מעבר מיושן נוחת באמצע-טבלה. קראו ל-ClearAllPageBreaks תחילה, ואז הוסיפו מחדש מעברים המחושבים ממוני השורות של המחולל עצמו במקום לטלא מיקומים ישנים. בחזית ה-XLS הבקרות המקבילות חיות על Sheet.PageSetup (כיוון, גודל נייר, שוליים, מחרוזות כותרת עליונה ותחתונה, התאם-לעמודים), עם RepeatRows ו-RepeatColumns מכסות את כותרות ההדפסה
בדיקת התוצאה לפני שהלקוח עושה זאת
באגי הגנה והדפסה חולקים תכונה אחת: קל לאמתם ביד וכמעט אף פעם אינם מאומתים. פתחו את הקובץ המחולל ב-Excel ובלו עליו תשעים שניות. הקלידו לתוך תא קלט ואשרו שהוא מקבל את ההקלדה; הקלידו לתוך תא נעול ואשרו שהנחיית ההגנה מופיעה; בדקו שנוסחה מוסתרת משאירה את שורת הנוסחאות ריקה. ואז הריצו תצוגה מקדימה של הדפסה מול מערך נתונים בגודל-ייצור, לא דגימה בת שלושים שורות, וקראו את ספירת העמודים, שורת הכותרת החוזרת ומספור הכותרת התחתונה. התצוגה המקדימה היא הצעד שמשלם על עצמו, משום שגאומטריית ההדפסה תלויה בהגדרות ללא רינדור על-מסך, ובהיעדר מדפסת פיזית זהו המקום היחיד שבו טעות התאמת קנה מידה הופכת אי-פעם לנראית
הגדרה אחרונה משלימה את הסקירה. FreezePane(ACol, ARow) שומר את בלוק הכותרת בתצוגה בעוד מבקר גולל. זוהי התנהגות מסך ולא התנהגות הדפסה, אך מבקר שופט את כל התוצר בבת אחת. וחוברת עבודה שמתחילה את חייה כפריסה מתוחזקת-מעצב מקבלת את רוב זה בחינם: זרימת העבודה של יצירת דוחות התבנית שומרת את הגדרת העמוד בתבנית, היכן שאדם כיוונן אותה מול מדפסת אמיתית, ומשאירה לקוד למלא את הנתונים ולהחיל מחדש את ההגנה ברגע שהפריסה מתייצבת
HotXLS היא ספריית גיליונות ילידית ב-Object Pascal ל-Delphi ול-C++Builder; עיון ה-API המלא של הגנה והגדרת עמוד נמצא בעמוד המוצר של HotXLS Component