Technical Article

HotXLS spojene ćelije i predlošci izvješća vođeni izgledom u Delphiju

Prođite kroz ćelije tek otvorenog predloška izvješća i spojeni naslov ponašat će se poput curenja. Pročitate A1 i dobijete "Quarterly Statement" (Tromjesečni izvještaj); pročitate B1 do F1, koji vidljivo stoje ispod istog natpisa, i ne dobijete ništa. Upišete vrijednost u C1 kako biste popravili zaglavlje i ona se nikada ne pojavi na zaslonu. Mreža nije izgubila vaše podatke. Ona radi točno ono što spajanje znači: i u XLS-u i u XLSX-u, spojeni pravokutnik prikazuje sadržaj jedne ćelije, gornjeg lijevog sidra, a ostatak tretira kao pokriveni prostor koji sadrži vrijednosti, ali ih nikada ne prikazuje. Korisnici Excela to nauče pokušajima i pogreškama. Generator izvješća to mora kodirati kao pravilo, jer je u generiranom kodu simptom prazno područje bez iznimke koja bi ukazala na problem. HotXLS, nativna Object Pascal biblioteka koja čita i piše oba formata Excela iz Delphija i C++Builder-a, izlaže tablicu spajanja dovoljno eksplicitno da možete programirati prema tom pravilu umjesto da ga ponovno otkrivate kroz tiket podrške.

Jedna vrijednost, jedno sidro

Spajanje je instrukcija za prikaz postavljena preko mreže koja ne mijenja oblik. Svaka pokrivena ćelija i dalje postoji u datoteci kao vlastiti utor; zapis o spajanju samo govori programu da iscrta sadržaj sidra preko cijelog pravokutnika. Ta razlika pokreće tri ponašanja koja vrijedi usvojiti prije nego što napišete bilo kakav kod za izgled. Čitanje pokrivene ćelije vraća njezinu vlastitu pohranjenu vrijednost, koja je za natpis koji ste izradili obično prazna, pa svaki kod koji ispituje spojeni naslov mora razriješiti i pročitati sidro. Pisanje u pokrivenu ćeliju uspijeva na razini datoteke, ali se nigdje ne prikazuje, što je zamka nevidljivog zaglavlja s početka članka. A razdvajanje (odspajanje) regije izlaže sve što se nalazilo ispod nje cijelo vrijeme, tako da se zalutala vrijednost zapisana u pokriveni prostor pretvara u vidljivi nedostatak onog dana kada netko razbije spajanje.

Na strani XLSX-a ta je tablica prvorazredni objekt. Sheet.MergedCells sadrži Add('A1:C1'), FindAt(Row, Col), DeleteAt i Items, a poziv za kojim najčešće posežete je FindAt: predajte mu bilo koju koordinatu i on vraća spojenu regiju koja pokriva tu ćeliju, ili nil kada ćelija stoji samostalno. To jedno pretraživanje temelj je za obje polovice ispravnog rukovanja spajanjem, sigurno čitanje i zaštitu od pisanja, a oba se pojavljuju kasnije.

Dva sučelja, dva idioma spajanja

HotXLS drži klasični BIFF8 .xls mehanizam i OOXML .xlsx mehanizam kao zasebne modele objekata, i oni spajanje definiraju drugačije jer potječu iz različitih konvencija. XLS sučelje slijedi idiom Excel COM-a: uzimate raspon iz indeksiranog svojstva s dva argumenta i pozivate Merge s OleVariant čija vrijednost odlučuje o geometriji koju ćete dobiti.

var
  Book: IXLSWorkbook;   // interface-counted: no manual Free
  Sh: IXLSWorksheet;
begin
  Book := TXLSWorkbook.Create;
  Sh := Book.Sheets[1];                 // XLS sheet collection is 1-based
  Sh.Range['A1', 'F1'].Merge(False);    // False = one merged block
  Sh.Cells.Item[1, 1].Value := 'Quarterly Statement';
  Sh.Range['A3', 'F4'].Merge(True);     // True = merge across: one merge per row
  Book.SaveAs('layout.xls');
end;

Argument predan metodi Merge je dio u kojem ljudi griješe. Preko raspona od dva retka, Merge(True) proizvodi dva neovisna spajanja od po jednog retka, što je Excelov "Merge Across" (spajanje poprijeko) i točno ono što želite za naslaganu traku zaglavlja koja treba zadržati svoje retke odvojivima. Merge(False) spaja cijeli pravokutnik u jedan blok. Raspon također javlja MergeCells kao zastavicu stanja, vraća regiju koja ga sadrži putem MergeArea i razrješava se pomoću Unmerge. XLSX sučelje izlaže iste operacije pod drugim nazivima: Sheet.MergeCells(Row1, Col1, Row2, Col2) prima cjelobrojne granice, TXLSXRange.Merge prihvaća ekvivalentnu varijantu Across, a zbirka MergedCells čuva rezultat.

Predložak koji raste s podacima

Stvarni predložak izvješća nije fiksna mreža. Zaglavlje i ukupne vrijednosti su fiksni, ali se odjeljak s pojedinostima između njih rasteže na sve što upit vrati. Uzorak koji dobro funkcionira zadržava jedan potpuno oblikovan redak pojedinosti u predlošku, klonira ga jednom po zapisu, a zatim otvara prazninu ispred bloka s ukupnim vrijednostima kako bi sve što je sidreno ispod skliznulo prema dolje bez gubitka oblikovanja.

Sheet.Range['A1:F1'].Merge;
Sheet.Cells[1, 1].Value := 'INVOICE #2026-0611';    // value goes to the anchor, A1
Sheet.RowHeight[1] := 28;
TitleFont := Book.Fonts.Add('Calibri', 16, True, False);
Sheet.Cells[1, 1].FontIndex := TitleFont + 1;        // pool index 0-based, cell side 1-based

// row 5 is the styled detail template line
for I := 0 to ItemCount - 1 do
  Sheet.CopyRange(5, 1, 5, 6, 6 + I, 1);             // styles and formulas travel with it

// open a gap above the totals block; content below shifts down
Sheet.InsertRows(6 + ItemCount, 1);
Sheet.Range['A1:F1'].SetBorders(xlsxEdgeOutline, xlsxBorderMedium);

Dva retka zaslužuju ponovni pogled. Dodjela fonta nosi pomak za jedan (off-by-one) koji stvara tihe probleme: Fonts.Add vraća poziciju skupa temeljenu na 0, dok ćelija pohranjuje referencu fonta temeljenu na 1 gdje 0 znači zadani font, stoga izostavljanje + 1 ne pokreće nikakvu iznimku, već samo oblikuje vaš naslov u krivom fontu. Drugi redak je CopyRange, koji premješta oblikovanje i formule zajedno s vrijednostima. To je cijeli razlog zašto se klonira ručno izrađeni redak predloška umjesto da se njegov izgled rekonstruira u kodu. Dizajner posjeduje izgled jednom, u predlošku; generator samo ulijeva podatke u njegove kopije.

Ta se podjela dodatno proširuje kada se višekratni izgled nalazi u vlastitoj radnoj knjizi, recimo na listu s trakama zaglavlja i podnožja koje se dijele među izvješćima. CopyRangeTo izvodi isto kloniranje preko granica radnog lista, primajući ciljni list i odredišne koordinate, tako da generator može zadržati jedan netaknuti predložak lista i utisnuti njegove regije u onoliko izlaznih listova koliko je potrebno za posao. Alternativa, mijenjanje predloška na licu mjesta i pokušaj njegova vraćanja nakon toga, vrsta je stvari koja radi sve do dana kada se izvođenje prekine na pola puta.

Što InsertRows pomiče, a što ne

Uzorak rasta predloška radi samo zato što je XLSX InsertRows strukturna izmjena, a ne miješanje ćelija. Kada otvori prazninu, premješta spojene regije, visine redaka, hiperveze, komentare, zamrznuta okna, raspone automatskog filtriranja, uvjetna oblikovanja, provjere valjanosti podataka, tablice, definirane nazive, sidra slika i sidra grafikona koji se nalaze ispod točke umetanja, a ne samo vrijednosti ćelija. To je ono što omogućuje bloku s ukupnim vrijednostima da stigne na svoj novi redak sa svojim spajanjima i formatima brojeva netaknutim, umjesto da stigne ogoljen.

Njegova dva dokumentirana ograničenja su ona oko kojih treba dizajnirati rješenje. Prilagodba formula ograničena je na list koji se uređuje: reference unutar tog lista se prepisuju, a formula na drugom listu koja pokazuje na pomaknuto područje također se prepisuje, ali prilagodba prati samo reference koje ciljaju na uređivani list, tako da bilo koja shema referenci između radnih knjiga zaslužuje vlastitu provjeru umjesto slijepog povjerenja. Drugo ograničenje je strože i nalazi se na strani XLS-a. Pivot tablice preživljavaju cikluse otvaranja i spremanja kao sirovi sačuvani zapisi, a ne kao modelirani objekti koje HotXLS može pomicati, tako da umetanje redaka ne premješta otisak pivot tablice. Svaki predložak koji izradite za .xls format trebao bi smjestiti svoje pivot regije daleko od bilo koje trake koja raste.

Odbijanje pisanja podataka u prostor izgleda

Kvar spojenih ćelija koji zapravo dođe do produkcije nije kozmetičke prirode. On je strukturne prirode: redak s pojedinostima odluta u spojenu traku izgleda, njegove vrijednosti slete u pokrivene ćelije i postanu nevidljive, a zbrojevi stupaca tiho prestanu odgovarati onome što svatko tko čita list može vidjeti. Budući da FindAt daje odgovor na pitanje o regiji pokrivanja za bilo koju koordinatu, generator može odbiti to pisanje u trenutku kada bi se trebalo dogoditi, umjesto da isporuči izvješće koje tiho prikazuje manji zbroj.

// refuse to write detail data into a merged layout region
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;

Ista provjera granica pripada bilo kojem mjestu gdje će korisnik kasnije sortirati ili filtrirati izlaz. Raspon sa spajanjima unutar sebe ne može se čisto sortirati jer sortiranje pomiče retke neovisno, a spajanje koje se proteže kroz više redaka nema jedan redak s kojim bi putovalo; Excel reagira s pogreškom ili zbrkanim izgledom. Uredan rad s izvješćima zahtijeva geografsku disciplinu. Ograničite spajanja na trake s naslovima, razdjelnike odjeljaka i blokove za potpis, a tabličnu sredinu lista držite ravnom. Članak o generiranju izvješća na temelju predloška razvija ovu podjelu između izgleda i podataka u potpuni tijek rada vođen rezerviranim mjestima (placeholders), a članak o uvjetnom oblikovanju i bogatom tekstu pokriva oblikovanje te ravne podatkovne trake.

Kako spajanja degradiraju pri izvozu

Spajanje je koncept radne knjige i svaki tekstualno orijentirani izvozni format poštuje ga u različitoj mjeri. Poznavanje ova tri ponašanja unaprijed štedi QA ciklus. Izvoz u HTML vjerno reproducira spajanja, emitirajući colspan i rowspan na jednoj tablici, tako da izvješće namijenjeno pregledniku zadržava svoj trakasti izgled. Izvoz u RTF uopće ne spaja stupce: tekst sidra slijeće u vlastitu ćeliju, a preostala širina spajanja izlazi kao prazne ćelije, što ostavlja široki naslov vizualno gurnut ulijevo u programu za obradu teksta. CSV nema koncept spajanja, tako da vrijednost sidra zauzima jedno polje, a svaka pokrivena ćelija se emitira kao prazno polje. Poučak za radnu knjigu koja također hrani izvoze s razdijeljenim vrijednostima je da sve što je bitno za sadržaj držite izvan spojene geometrije; članak o izvozu u CSV, TSV i HTML detaljno prolazi kroz svaki format.

Jedno umirenje za sve koji ovo vagaju u odnosu na veličinu datoteke: spajanja ne koštaju gotovo ništa na razini izvješća. Tablica spajanja je sićušna u usporedbi s podacima o ćelijama, a čitanje pokrivene ćelije i dalje ide kroz FindAt umjesto skeniranja. Pritisak na performanse kod velikih radnih knjiga dolazi s drugih mjesta, uglavnom od rasta skupa stilova i memorije koju drži putanja spremanja, što članak o performansama velikih radnih knjiga izravno obrađuje. Oba API-ja za spajanje, strukturne operacije uređivanja i demo primjeri predložaka dolaze s HotXLS komponentom.