Articol tehnic

Celule îmbinate HotXLS și șabloane de raport bazate pe machetă în Delphi

Iterează celulele unui șablon de raport tocmai deschis și un titlu îmbinat se poartă ca o scurgere. Citești A1 și primești „Quarterly Statement”; citești de la B1 până la F1, care stau vizibil sub același banner, și nu primești nimic. Scrii o valoare în C1 ca să peticești antetul și ea nu apare niciodată pe ecran. Grila nu ți-a pierdut datele. Face exact ce înseamnă o îmbinare: atât în XLS, cât și în XLSX, un dreptunghi îmbinat randează conținutul unei singure celule, ancora din stânga sus, și tratează restul drept spațiu acoperit, care ține valori, dar nu le arată niciodată. Utilizatorii de Excel absorb asta prin încercare și eroare. Un generator de rapoarte trebuie să o codifice ca regulă, pentru că în codul generat simptomul este o regiune goală, fără nicio excepție care să te ducă la ea. HotXLS, o bibliotecă nativă Object Pascal care citește și scrie ambele formate Excel din Delphi și C++Builder, scoate la suprafață tabela de îmbinări destul de explicit încât să poți programa după regulă, în loc să o redescoperi într-un tichet de suport

O valoare, o ancoră

O îmbinare este o instrucțiune de afișare așezată peste o grilă care nu își schimbă forma. Fiecare celulă acoperită există în continuare în fișier ca slot propriu; înregistrarea de îmbinare doar îi spune consumatorului să picteze conținutul ancorei peste tot dreptunghiul. Distincția aceasta produce trei comportamente pe care merită să le interiorizezi înainte să scrii cod de aspect. Citirea unei celule acoperite întoarce valoarea ei proprie, care, pentru un banner construit de tine, este de obicei goală, așa că orice cod care inspectează un titlu îmbinat trebuie să rezolve ancora și să citească de acolo. Scrierea într-o celulă acoperită reușește la nivel de fișier și nu apare nicăieri, adică exact capcana antetului invizibil din deschidere. Iar desfacerea unei regiuni scoate la vedere tot ce stătea sub ea în tot acest timp, așa că o valoare rătăcită, scrisă în spațiu acoperit, se preface în defect vizibil în ziua în care cineva dizolvă îmbinarea

Diagramă a unui banner unit HotXLS, în care celulele acoperite își păstrează propriile sloturi, în timp ce citirile se rezolvă la ancora A1 în foile de calcul Delphi
HotXLS păstrează fiecare celulă acoperită ca un slot real și repictează doar ancora, astfel încât citirile se rezolvă prin A1, în timp ce scrierile în spațiul acoperit rămân invizibile până la un unmerge

Pe partea XLSX, tabela aceea este un obiect de primă clasă. Sheet.MergedCells poartă Add('A1:C1'), FindAt(Row, Col), DeleteAt și Items, iar apelul la care ajungi cel mai des este FindAt: dă-i orice coordonată și îți întoarce regiunea îmbinată care acoperă celula aceea sau nil când celula stă de una singură. Acea unică căutare este temelia pentru ambele jumătăți ale unei tratări corecte a îmbinărilor, citirea sigură și garda la scriere, iar amândouă apar mai jos

Două fațade, două idiomuri de îmbinare

HotXLS păstrează motorul clasic BIFF8 .xls și motorul OOXML .xlsx ca modele de obiecte separate, iar ele scriu îmbinarea diferit pentru că se trag din convenții diferite. Fațada XLS urmează idiomul COM din Excel: iei un interval dintr-o proprietate indexată cu două argumente și apelezi Merge cu un OleVariant a cărui valoare hotărăște geometria la care ajungi

var
  Book: IXLSWorkbook;   // numărat prin interfață: fără Free manual
  Sh: IXLSWorksheet;
begin
  Book := TXLSWorkbook.Create;
  Sh := Book.Sheets[1];                 // colecția de foi XLS este numerotată de la 1
  Sh.Range['A1', 'F1'].Merge(False);    // False = un singur bloc îmbinat
  Sh.Cells.Item[1, 1].Value := 'Quarterly Statement';
  Sh.Range['A3', 'F4'].Merge(True);     // True = îmbinare transversală: o îmbinare pe rând
  Book.SaveAs('layout.xls');
end;

Argumentul lui Merge este partea pe care oamenii o greșesc. Peste un interval de două rânduri, Merge(True) produce două îmbinări independente de câte un rând, adică „Merge Across” din Excel și exact ce vrei pentru o bandă de antet suprapusă, care trebuie să își păstreze rândurile separabile. Merge(False) topește tot dreptunghiul într-un singur bloc. Intervalul mai raportează MergeCells ca indicator de stare, întoarce regiunea care îl conține prin MergeArea și se dizolvă singur cu Unmerge. Fațada XLSX expune aceleași operații sub alte nume: Sheet.MergeCells(Row1, Col1, Row2, Col2) primește limite întregi, TXLSXRange.Merge acceptă varianta echivalentă cu Across, iar colecția MergedCells ține rezultatul

Un șablon care crește odată cu datele lui

Un șablon de raport adevărat nu este o grilă fixă. Antetul și totalurile sunt fixe, dar secțiunea de detaliu dintre ele se întinde după cât întoarce interogarea. Tiparul care rezistă păstrează în șablon un singur rând de detaliu complet stilizat, îl clonează câte o dată pentru fiecare înregistrare și apoi deschide un gol înaintea blocului de totaluri, ca tot ce este ancorat mai jos să coboare fără să își piardă formatarea

Șablon de raport HotXLS crescând în Delphi: rândul de detalii stilizat este clonat per înregistrare, iar InsertRows deschide un gol astfel încât blocul de totaluri alunecă în jos cu uniunile intacte
Clonarea rândului de detaliu stilizat își duce stilurile și formulele în fiecare copie, iar InsertRows alunecă apoi banda de totaluri în jos cu fuziunile și formatele intacte
Sheet.Range['A1:F1'].Merge;
Sheet.Cells[1, 1].Value := 'INVOICE #2026-0611';    // valoarea merge la ancoră, A1
Sheet.RowHeight[1] := 28;
TitleFont := Book.Fonts.Add('Calibri', 16, True, False);
Sheet.Cells[1, 1].FontIndex := TitleFont + 1;        // index de fond de la 0, partea celulei de la 1

// rândul 5 este linia-șablon de detaliu, deja stilizată
for I := 0 to ItemCount - 1 do
  Sheet.CopyRange(5, 1, 5, 6, 6 + I, 1);             // stilurile și formulele merg odată cu el

// deschide un gol deasupra blocului de totaluri; conținutul de dedesubt coboară
Sheet.InsertRows(6 + ItemCount, 1);
Sheet.Range['A1:F1'].SetBorders(xlsxEdgeOutline, xlsxBorderMedium);

Două linii merită o a doua privire. Atribuirea fontului poartă un decalaj cu o unitate care mușcă tăcut: Fonts.Add întoarce o poziție din fond numerotată de la 0, în timp ce o celulă stochează o referință de font numerotată de la 1, unde 0 înseamnă fontul implicit, așa că renunțarea la + 1 nu ridică nimic, doar îți stilizează titlul cu alt caracter tipografic. Cealaltă linie este CopyRange, care mută formatarea și formulele odată cu valorile. Acesta este întregul motiv pentru care clonezi un rând-șablon construit manual, în loc să îi reconstruiești înfățișarea în cod. Un proiectant deține aspectul o singură dată, în șablon; generatorul doar toarnă date în copii ale lui

Despărțirea aceasta scalează și mai bine când aspectul reutilizabil trăiește în propriul registru de lucru, să zicem o foaie cu benzi de antet și de subsol partajate între rapoarte. CopyRangeTo face aceeași clonare peste granițele dintre foile de calcul, primind o foaie țintă plus coordonate de destinație, așa că un generator poate ține o singură foaie-șablon neatinsă și îi poate ștampila regiunile în câte foi de ieșire are nevoie o lucrare. Alternativa, adică schimbarea șablonului pe loc și încercarea de a-l reface după aceea, este genul de lucru care merge până în ziua în care o rulare se întrerupe la jumătate

Ce mută InsertRows și ce nu

Tiparul de șablon care crește funcționează doar pentru că InsertRows din XLSX este o editare structurală, nu o amestecare de celule. Când deschide un gol, mută regiunile îmbinate, înălțimile de rând, hyperlinkurile, comentariile, panourile înghețate, intervalele de autofiltru, formatele condiționate, validările de date, tabelele, numele definite, ancorele de imagine și ancorele de grafic care stau sub punctul de inserare, nu doar valorile din celule. Asta face ca blocul de totaluri să ajungă la noul lui rând cu îmbinările și formatele de număr intacte, nu jupuit

Cele două limite documentate ale lui sunt cele în jurul cărora trebuie să proiectezi. Ajustarea formulelor este limitată la foaia editată: referințele din interiorul acelei foi sunt rescrise, iar o formulă de pe altă foaie care indică în zona deplasată este și ea rescrisă, dar ajustarea urmărește doar referințele care țintesc foaia editată, așa că orice schemă de referințe între registre de lucru merită un audit propriu, nu încredere oarbă. A doua limită este mai tăioasă și se află pe partea XLS. Tabelele pivot supraviețuiesc ciclurilor de deschidere și salvare ca înregistrări brute păstrate, nu ca obiecte modelate pe care HotXLS le poate muta, așa că inserarea de rânduri nu deplasează amprenta unui pivot. Orice șablon pe care îl construiești pentru formatul .xls ar trebui să își parcheze regiunile de pivot bine departe de orice bandă care crește

Refuzul de a scrie date în spațiul de aspect

Eșecul de celule îmbinate care ajunge cu adevărat în producție nu este cel cosmetic. Este unul structural: un rând de detaliu alunecă într-o bandă de aspect îmbinată, valorile lui aterizează în celule acoperite și devin invizibile, iar totalurile pe coloane încetează discret să se mai potrivească cu ce poate vedea oricine citește foaia. Fiindcă FindAt răspunde la întrebarea despre regiunea acoperitoare pentru orice coordonată, generatorul poate refuza scrierea aceea chiar în clipa în care s-ar produce, în loc să livreze un raport care numără tăcut în minus

// refuză scrierea datelor de detaliu într-o regiune de aspect îmbinată
if Sheet.MergedCells.FindAt(Row, 1) <> nil then
  raise Exception.CreateFmt('row %d overlaps a merged layout region', [Row]);
Sheet.Cells[Row, 1].Value := Detail.Description;

Aceeași verificare de limită își are locul oriunde un utilizator va sorta sau va filtra mai târziu rezultatul. Un interval cu îmbinări înăuntru nu poate fi sortat curat, pentru că sortarea mută rândurile independent, iar o îmbinare care se întinde pe mai multe rânduri nu are un singur rând cu care să călătorească; Excel răspunde cu o eroare sau cu un aspect răvășit. Disciplina care ține rapoartele corecte este geografică. Ține îmbinările în benzile de titlu, în despărțitoarele de secțiune și în blocurile de semnătură, iar mijlocul tabelar al foii păstrează-l plat. Articolul despre generarea de rapoarte pe bază de șabloane dezvoltă despărțirea aceasta între aspect și date într-un flux complet condus de substituenți, iar articolul despre formatarea condiționată și textul îmbogățit tratează stilizarea acelei benzi plate de date

Cum se degradează îmbinările la ieșire

O îmbinare este un concept de registru de lucru, iar fiecare format de export orientat pe text o onorează în altă măsură. Cunoașterea celor trei comportamente din capul locului economisește un ciclu de QA. Exportul HTML reproduce fidel îmbinările, emițând colspan și rowspan pe un singur tabel, așa că un raport destinat browserului își păstrează aspectul cu benzi. Exportul RTF nu se întinde deloc pe coloane: textul ancorei aterizează în propria lui celulă, iar restul lățimii îmbinării iese ca celule goale, ceea ce lasă un titlu lat împins vizual spre stânga într-un procesor de text. CSV nu are noțiunea de îmbinare, așa că valoarea ancorei ocupă un câmp, iar fiecare celulă acoperită iese ca un câmp gol. Concluzia pentru un registru de lucru care alimentează și exporturi delimitate este să ții orice element portant în afara geometriei îmbinate; articolul despre exportul CSV, TSV și HTML parcurge în detaliu fiecare format

Titlu unit HotXLS exportat din Delphi în HTML cu colspan și rowspan, în RTF fără span-uri și în CSV ca câmpuri aplatizate
Același titlu fuzionat supraviețuiește exportului HTML prin colspan și rowspan, se degradează în RTF la o celulă singulară blocată la stânga, și se aplatizează la o valoare plus câmpuri goale în CSV

O liniștire pentru oricine cântărește asta față de dimensiunea fișierului: îmbinările nu costă mai nimic la scara unui raport. Tabela de îmbinări este minusculă pe lângă datele din celule, iar citirea unei celule acoperite trece tot prin FindAt, fără scanare. Presiunea de performanță de pe registrele de lucru mari vine din altă parte, mai ales din creșterea fondului de stiluri și din memoria pe care o ține calea de salvare, lucruri de care se ocupă direct articolul despre performanța registrelor de lucru mari. Ambele API-uri de îmbinare, operațiile de editare structurală și demonstrațiile cu șabloane vin la pachet cu HotXLS Delphi Component