כלל עיצוב מותנה ב-OOXML הוא שני דברים נפרדים שעונדים שם אחד. התנאי (השוואה, נוסחה, התאמת טקסט) מחליט אילו תאים מתאימים. המראה (רשומת differential format, dxf במונחי ECMA-376) מחליט איך התאים האלה נראים. הדיאלוג של Excel מסתיר את התפר בכך שהוא גורם לך למלא את שניהם בבת אחת. HotXLS לא. צור כלל cellIs מ-Delphi ודלג על הסגנון, והכלל תקף, הטווח נכון, הנוסחה מחזירה true בדיוק על התאים הנכונים, ושום דבר לא משנה צבע, כי ההוראה של הכלל הייתה "true, paint nothing." הפער הזה בין תנאי לתוצאה הוא הדבר הראשון שצריך לתקן, והוא אחראי לרוב הכללים שנראים נכונים ב-Manage Rules ובכל זאת לא מדגישים כלום
HotXLS כותב עיצוב מותנה באופן טבעי לתוך קובצי BIFF8 .xls וגם OOXML .xlsx, וכך גם לריצות טקסט עשיר ומודל סגנון-תא משותף. שלוש התכונות חולקות יותר חיווט ממה שמשטח ה-API השטוח מרמז, והמקומות שבהם הפלט סוטה מהכוונה הם בדרך כלל המפרקים ביניהן
תנאי זקוק לתוצאה: סגנון ה-dxf
בגיליון XLSX, כללי השוואה באים מ-AddConditionalFormat, שלוקח טווח, אופרטור מ-TXLSXCfOperator, ונוסחה או ערך ליטרלי, ואז מחזיר את האינדקס של הכלל החדש בתוך אוסף ConditionalFormats של הגיליון. אובייקט הכלל באינדקס הזה חושף מאפיין Style, ושם חיה ההדגשה. קבע עליו מילוי והתאים המתאימים יקבלו את המילוי. השאר אותו ללא שינוי ובנית את הכלל הבלתי-נראה שתואר לעיל
var
Book: TXLSXWorkbook;
Sheet: TXLSXWorksheet;
Idx: Integer;
begin
Book := TXLSXWorkbook.Create;
try
Book.Open('kpi.xlsx');
Sheet := Book.Sheets[0];
// שונות שלילית: מילוי אדום בהיר
Idx := Sheet.AddConditionalFormat('D2:D200', xlsxCfOpLessThan, '0');
Sheet.ConditionalFormats[Idx].Style.SetFillBgColor($FFFFC7CE);
// מזהי הזמנה כפולים מסומנים באותו אופן
Idx := Sheet.AddCondFormatDuplicateValues('A2:A200');
Sheet.ConditionalFormats[Idx].Style.SetFillBgColor($FFFFEB9C);
// כלל נוסחה מותאמת אישית: הדגש שורות שבהן בפועל חסרים 90% מהיעד
Idx := Sheet.AddCondFormatExpression('B2:B200', '$C2<$B2*0.9');
Sheet.ConditionalFormats[Idx].Style.SetFillBgColor($FFFFC7CE);
Book.SaveAs('kpi-flagged.xlsx');
finally
Book.Free;
end;
end;
צבעים כאן הם ערכי ARGB של 32 סיביות, כך ש-$FFFFC7CE הוא ה"אדום בהיר" של Excel שאתה מכיר מהדיאלוג, עם בית אלפא אטום לחלוטין יושב לפני ה-RGB. כל סוג כלל שמופעל על תנאי לכל-תא עוקב אחר אותו דפוס צור-ואז-סגנן. כללי התאמת הטקסט (AddCondFormatContainsText, AddCondFormatBeginsWith, AddCondFormatEndsWith) מחזירים אינדקס שאותו אתה מסגנן אחר כך, וכך גם AddCondFormatTop10, AddCondFormatAboveAverage, וגלאי הריק והשגיאה. למד את הדפוס פעם אחת וכל משפחת הטקסט-והשוואה מתנהגת אותו דבר
עמודות נתונים, סולמות צבעים וערכות סמלים מציירות את עצמן
סוגי הכללים החזותיים עובדים בדרך הפוכה. הם נושאים את המראה שלהם בתוך הגדרת הכלל ומתעלמים לחלוטין ממאפיין Style. הקצה מילוי לכלל עמודת נתונים ושום דבר לא קורה, מה שנקרא כמו באג עד שהטקסונומיה נכנסת: AddCondFormatDataBar לוקח את צבע העמודה כארגומנט ישיר, סולמות הצבע הדו- והתלת-נקודתיים לוקחים את צבעי נקודות הקצה שלהם באותו אופן, ו-AddCondFormatIconSet בוחר אחת מ-26 סוגי ערכות סמלים כמו icsTrafficLights3. אין כאן רשומת סגנון נפרדת לשכוח, כי אין בכלל רשומת סגנון נפרדת
הפרמטרים שכדאי לחשוב עליהם בקריאות האלה הם עוגני הערך, מוקלדים כ-TXLSCfValueKind. נקודת קצה של עמודה או סולם יכולה לשבת במינימום או מקסימום הטווח, במספר ליטרלי, באחוז או באחוזון, או בתוצאה של נוסחה. ברירות המחדל, מינימום-טווח ומקסימום-טווח, מתנהגות יפה על נתוני דמו נקיים ואז בוגדות בך על נתונים אמיתיים עם חריגים: ערך בורח אחד מותח את הסולם ומשטח כל עמודה אחרת לקצוות. כאשר לוח מכוון לקריאה לאורך תקופות, עוגן את נקודות הקצה למספרים קבועים או אחוזונים במקום, כך שחצי עמודה במרץ תהיה אותה כמות כמו חצי עמודה באפריל. עמודה בסקייל-אוטומטי ניתנת להשוואה רק לעצמה
הכותב של XLS מכסה ארבעה סוגי כללים, לא יותר
צד ה-BIFF8 המורשת אינו מראה קטן יותר של צד ה-XLSX; הוא תת-קבוצה מכוונת. ה-facade של XLS יכול ליצור בדיוק ארבע צורות כלל מותנה, עמודות נתונים, סולמות דו-צבע, סולמות תלת-צבע, וערכות סמלים, הנפלטות כרשומות CF12 לתוך הזרם. אין לו API יצירה לכללי cellIs, ביטוי או טקסט. כללים מסוגים אלה שכבר חיים בקובץ שאתה פותח נקראים, נשמרים, ונכתבים בחזרה ללא שינוי, כך שפתיחה ושמירה-מחדש של .xls של לקוח לעולם לא תפגע בעיצוב שנשא אותו פנימה. מה שאינך יכול לעשות הוא לייצר הדגשת סף מאפס לתוך .xls. הבחירות שם הן לזייף את זה עם מילויי תא רגילים המחושבים בקוד, או להפוך את התוצר ל-.xlsx, שם כל משפחת הכללים המלאה על השולחן
זהו אילוץ שצריך להכריע בו לפני ששכבת הנתונים קיימת, לא אחריה, כי הוא משנה את החלטת פורמט הקובץ לכל דבר בצורת לוח-בקרה. צוות שבחר ב-.xls לתאימות ואז מפרט דוח KPI עם ספי cellIs בחר שני דברים שלא מתאימים זה לזה, והזמן הזול יותר להבחין בזה הוא בהחלטת הפורמט ולא שלושה שבועות לתוך הבנייה
ערימת כללים, עדיפות וטווחים חופפים
לוחות בקרה אמיתיים רק לעיתים רחוקות מריצים כלל אחד לכל טווח. עמודת שונות עשויה לשאת עמודת נתונים לגודל, כלל cellIs לסף הקשיח, וכלל ביטוי ברמת-השורה מעל שניהם להסלמות. כל TXLSXConditionalFormat חושף ערך Priority, ו-Excel פותר כללים מתחרים בסדר עדיפות. כאשר שני כללים רוצים לצבוע את אותו תא, המנצח מוכרע על ידי מספר שאתה קובע, לא על ידי איזה סדר שבו סוקר קורא במקרה בדיאלוג Manage Rules
התייחס לעדיפות כפי שתוכנית ציור מתייחסת ל-z-order. הקצה אותה בכוונה בכל מקום ששני כללים יכולים להגיע לאותם תאים, והשאר רווחים בין הערכים כך שכלל מאוחר יותר ייכנס בלי למספר מחדש את השאר. כאשר כללים אינם יכולים להתנגש, נניח עמודת נתונים המוגבלת לעמודה E וכלל טקסט המוגבל לעמודה G, סדר היצירה בסדר והעדיפות אינה ראויה לתשומת הלב. השקיע את תשומת הלב הזו בגבולות הטווח במקום, כי הבאגים היקרים כאן כמעט אף פעם אינם היפוכי עדיפות. הם טווחים כמו B2:B200 על דוח שגדל ל-350 שורות, שבו הזנב הלא-מכוסה מוצג כתאים רגילים שנראים בדיוק כמו נתונים בריאים. גזור כל טווח כלל מאותו ערך ספירת-שורות-סופית שמניע את סדרות התרשים וטווחי האימות במקומות אחרים בחוברת העבודה, והזנב מפסיק ליפול
אחת הרגלי אימות משתלמת. לאחר יצירה, פתח את הקובץ ב-Excel, בחר את הטווח המעוצב, ועבור על Manage Rules פעם אחת לכל שינוי תבנית. עיצוב מותנה הוא אחד התחומים הבודדים שבהם המצייר הסמכותי היחיד הוא היישום שצורך את הקובץ, כך שבדיקת יחידה על ה-XML מוכיחה שהכלל נכתב, לא ש-Excel מצייר אותו כפי שהתכוונת. דקה של מבט-עין סוגרת את הפער הזה
טקסט עשיר: פורמטים רבים בתוך תא אחד
תא טקסט-עשיר במודל XLSX מחזיק רשימה של ריצות, שבה כל ריצה היא מקטע טקסט בתוספת תכונות הפונט שלו. אתה בונה את הרשימה בצד כאובייקט TXLSXRichText, מוסיף לה ריצות, ואז מחבר את כל הדבר לתא. כלל הבעלות הוא החלק שנושך. השמה ל-Cell.RichText מעבירה בעלות של אובייקט זה לתא, והתא משחרר אותו במהלך ההרס של עצמו. שחרר אותו גם בעצמך ויש לך double-free, מהסוג שנשאר שקט דרך הריצה שגרמה לו וצץ כקריסה איפשהו לא-קשור הרבה יותר מאוחר
var
Rich: TXLSXRichText;
Run: TXLSXRichTextRun;
begin
Rich := TXLSXRichText.Create;
Rich.AddRunText('Status: ');
Run := Rich.AddRunText('OVERDUE');
Run.Bold := True;
Run.Color := $FFC00000;
Run.ColorIsAuto := False;
Run := Rich.AddRunText(' (escalated to regional manager)');
Run.Italic := True;
Sheet.Cells[2, 7].RichText := Rich; // הבעלות עוברת לתא: אל תבצע Free
end;
ה-ColorIsAuto := False המפורש אינו קישוט אופציונלי. ריצה נושאת דגל צבע-אוטומטי, והקצאת צבע מכובדת רק לאחר שהדגל הזה מנוקה. קבע Color ושכח ColorIsAuto והריצה יוצאת מודגשת אבל בעקשנות שחורה, ללא שגיאה שתצביע על הסיבה. ריצות גם תומכות קו-חוצה, וריאנטי הקו-תחתון, ויישור אנכי לכתב-עילי וכתב-תחתי, בעוד PlainText משטחת את כל הרשימה בחזרה למחרוזת יחידה כאשר אתה צריך לייצא או להשוות את תוכן הטקסט
טקסט עשיר ברמת התא הוא XLSX-בלבד. ל-facade של XLS אין API ציבורי לכתיבתו, אם כי ריצות זמינות שם בהערות ותיבות טקסט דרך TextRuns, ומחרוזות עשירות הנקראות מ-.xls קיים שורדות round-trip שלם. המשיכה זהה לעיצוב מותנה: כל דבר שמערב פורמטים בתוך תא שייך לכותב ה-XLSX
מאגר הסגנון וה-off-by-one שמופץ
עיצוב תא רגיל במודל XLSX רץ דרך אוספים משותפים על חוברת העבודה. Fonts.Add, Fills.AddSolid, ו-Borders.Add כל אחד רושם הגדרה ומחזיר את האינדקס שלה במאגר. אינדקסים אלה הם 0-בסיסיים. מאפייני צד-התא שצורכים אותם, כגון FontIndex, שומרים 0 ל-"default", כך שהערך שאתה מקצה לתא הוא אינדקס המאגר ועוד אחד:
HeaderFont := Book.Fonts.Add('Calibri', 11, True, False); // pool index, 0-based
for Col := 1 to 6 do
Sheet.Cells[1, Col].FontIndex := HeaderFont + 1; // cell index, 1-based
השמט את ה-+ 1 וכל כותרת חוזרת לפונט ברירת המחדל. אין שום חריג ושום אזהרה, רק חוברת עבודה שנראית כאילו אף אחד לא עיצב אותה. הטעות מסדר-שני מתחבאת בתוך הלולאה: קריאה ל-Fonts.Add פעם אחת לכל שורה. הגדרות פונט זהות מבוטלות-כפילויות, כך שהקובץ אינו מושחת, אבל העבודה מבוזבזת, ומאגר היישור בפרט מחזיר אובייקט חדש בכל קריאה במקום לקפל כפילויות. בנה את הסגנונות המעטים פעם אחת לפני הלולאה ועשה שימוש חוזר באינדקסים שלהם. בדוחות של מאה-אלף-שורות השינוי היחיד הזה הוא אחד המנופים המכוסים ב-כיוון ביצועי חוברת עבודה גדולה עבור HotXLS. כאשר אתה צריך רק מראה סמנטי מובנה, שני ה-facade-ים חושפים ApplyBuiltinStyle על טווחים, שממפה לסגנונות Good, Bad, Neutral ו-accent המובנים של Excel בלי שתיגע במאגרים כלל
עיצוב מותנה, טקסט עשיר וסגנונות משותפים הם המייל האחרון של דוח, המופעלים לאחר שמודל הנתונים והפריסה מיושבים, ושלבים מוקדמים יותר אלה הם הנושא של יצירת דוחות מבוססי-תבנית עם HotXLS. ההפניה המלאה של כללים, ריצות וסגנון חיה בעמוד המוצר של HotXLS Delphi Component