Tekninen artikkeli

HotXLS-yhdistetyt solut ja asetteluun perustuvat raporttipohjat Delphissä

Kun käyt vastikään avatun raporttipohjan soluja läpi, yhdistetty otsikko käyttäytyy kuin vuoto. Luet solun A1 ja saat arvon "Quarterly Statement"; luet solut B1F1, jotka näkyvät saman otsakepalkin alla, etkä saa mitään. Kirjoitat arvon soluun C1 korjataksesi otsikon, eikä se koskaan näy näytöllä. Ruudukko ei kadottanut tietojasi. Se tekee täsmälleen sen, mitä yhdistäminen tarkoittaa: sekä XLS- että XLSX-muodossa yhdistetty suorakulmio näyttää yhden solun, vasemman yläkulman ankkurin, sisällön koko alueella ja käsittelee muita soluja peitettynä tilana, joka voi sisältää arvoja mutta ei koskaan näytä niitä. Excelin käyttäjät omaksuvat tämän kokeilemalla ja erehtymällä. Raporttigeneraattorin on ilmaistava se sääntönä, sillä tuotetussa koodissa oire on tyhjä alue ilman poikkeusta, joka johtaisi sen alkuperään. HotXLS, Delphistä ja C++Builderista molempia Excel-muotoja lukeva ja kirjoittava natiivi Object Pascal -kirjasto, tuo yhdistämistaulukon näkyviin riittävän selvästi, jotta voit ohjelmoida säännön mukaan etkä joudu löytämään sitä uudelleen tukipyynnössä

Yksi arvo, yksi ankkuri

Yhdistäminen on ruudukon päälle asetettava näyttöohje, joka ei muuta ruudukon muotoa. Jokainen peitetty solu on edelleen tiedostossa omana paikkanaan; yhdistämistietue vain käskee kuluttajaa piirtämään ankkurin sisällön suorakulmion alueelle. Tämä ero ohjaa kolmea toimintaa, jotka kannattaa sisäistää ennen asettelukoodin kirjoittamista. Peitetyn solun lukeminen palauttaa sen oman tallennetun arvon, joka luomassasi otsakepalkissa on tavallisesti tyhjä, joten yhdistettyä otsikkoa tarkastelevan koodin on selvitettävä ja luettava ankkuri. Peitettyyn soluun kirjoittaminen onnistuu tiedostotasolla mutta ei näy missään, mikä on johdannon näkymätön otsikko -ansa. Yhdistämisen purkaminen taas paljastaa sen alla koko ajan olleet arvot, joten peitettyyn tilaan kirjoitettu harha-arvo muuttuu näkyväksi virheeksi sinä päivänä, kun joku purkaa yhdistämisen

Kaavio HotXLS:n yhdistetystä bannerista, jossa katetut solut pitävät omat paikkansa, kun taas lukemat ratkeavat ankkuriin A1 Delphi-laskentataulukoissa
HotXLS pitää jokaisen peitetyn solun todellisena slotina ja maalaa uudelleen vain ankkurin, joten luvut ratkeavat A1:n kautta, kun taas kirjoitukset peitettyyn tilaan pysyvät näkymättöminä ennen yhdistyksen purkamista

XLSX-puolella taulukko on ensiluokkainen objekti. Sheet.MergedCells sisältää metodit Add('A1:C1'), FindAt(Row, Col), DeleteAt ja Items, ja useimmin tarvitsemasi kutsu on FindAt: anna sille mikä tahansa koordinaatti, niin se palauttaa solun peittävän yhdistetyn alueen tai nil-arvon, jos solu on yksin. Tämä yksi haku on oikean yhdistettyjen solujen käsittelyn molempien puolien, turvallisen luvun ja kirjoitussuojan, perusta, ja molemmat tulevat myöhemmin vastaan

Kaksi julkisivua, kaksi yhdistämistapaa

HotXLS pitää klassisen BIFF8-.xls-moottorin ja OOXML-.xlsx-moottorin erillisinä objektimalleina, ja ne ilmaisevat yhdistämisen eri tavoin, koska niiden taustalla ovat eri käytännöt. XLS-julkisivu noudattaa Excel COM -idiomia: haet alueen kaksiperusteisesta indeksoidusta ominaisuudesta ja kutsut Merge-metodia OleVariant-arvolla, jonka sisältö määrää syntyvän geometrian

var
  Book: IXLSWorkbook;   // interface-counted: no manual Free
  Sh: IXLSWorksheet;
begin
  Book := TXLSWorkbook.Create;
  Sh := Book.Sheets[1];                 // XLS-taulukkojen kokoelma on 1-pohjainen
  Sh.Range['A1', 'F1'].Merge(False);    // False = yksi yhdistetty lohko
  Sh.Cells.Item[1, 1].Value := 'Quarterly Statement';
  Sh.Range['A3', 'F4'].Merge(True);     // True = yhdistä poikki: yksi yhdistys riviä kohden
  Book.SaveAs('layout.xls');
end;

Merge-metodin argumentti on kohta, jonka ihmiset ymmärtävät usein väärin. Kaksirivisellä alueella Merge(True) tuottaa kaksi itsenäistä yksirivistä yhdistämistä, mikä on Excelin "Merge Across" ja juuri sitä, mitä tarvitset pinotulle otsakepalkille, jonka rivit haluat pitää erillisinä. Merge(False) sulauttaa koko suorakulmion yhdeksi lohkoksi. Alue raportoi myös tilalipun MergeCells, palauttaa sisältävän alueen MergeArea-ominaisuuden kautta ja purkaa yhdistämisen Unmerge-metodilla. XLSX-julkisivu tarjoaa samat toiminnot eri nimillä: Sheet.MergeCells(Row1, Col1, Row2, Col2) ottaa kokonaislukurajat, TXLSXRange.Merge hyväksyy vastaavan Across-variantin ja MergedCells-kokoelma sisältää tuloksen

Datansa mukana kasvava pohja

Todellinen raporttipohja ei ole kiinteä ruudukko. Otsikko ja loppusummat ovat kiinteitä, mutta niiden välinen tietojaosto venyy kyselyn palauttaman määrän mukaan. Kestävä toimintamalli pitää pohjassa yhden täysin muotoillun tietorivin, kloonaa sen kerran kutakin tietuetta kohden ja avaa sitten välin loppusummalohkon eteen, jotta kaikki sen alapuolelle ankkuroidut osat siirtyvät alaspäin menettämättä muotoilujaan

HotXLS-raporttimallin kasvu Delphissä: tyylitelty tietorivi kloonataan tietuetta kohden ja InsertRows avaa raon, jolloin kokonaisuuslohko liukuu alas yhdistelmät ehjinä
Tyylitetyn rivin kloonaaminen kantaa sen tyylit ja kaavat jokaiseen kopioon, ja InsertRows liu'uttaa sitten summa-kaistan alas yhdistelyjen ja muotoilujen säilyessä ennallaan
Sheet.Range['A1:F1'].Merge;
Sheet.Cells[1, 1].Value := 'INVOICE #2026-0611';    // arvo siirtyy ankkuriin, 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

// rivi 5 on tyylitelty tietorivin mallirivi
for I := 0 to ItemCount - 1 do
  Sheet.CopyRange(5, 1, 5, 6, 6 + I, 1);             // tyylit ja kaavat kulkevat mukana

// avaa aukko loppusummalohkon yläpuolelle; sisältö alapuolella siirtyy alas
Sheet.InsertRows(6 + ItemCount, 1);
Sheet.Range['A1:F1'].SetBorders(xlsxEdgeOutline, xlsxBorderMedium);

Kaksi riviä ansaitsee toisen tarkastelun. Fontin määrityksessä on hiljaa pureva yhden indeksin virhe: Fonts.Add palauttaa 0-pohjaisen paikan fonttivarannossa, kun taas solu tallentaa 1-pohjaisen fonttiviittauksen, jossa 0 tarkoittaa oletusfonttia. Jos + 1 jää pois, mitään poikkeusta ei synny, vaan otsikkosi saa väärän kirjasinlajin. Toinen rivi on CopyRange, joka siirtää muotoilun ja kaavat arvojen mukana. Juuri siksi käsin rakennetun pohjarivin kloonaaminen on parempi kuin sen ulkoasun luominen uudelleen koodissa. Suunnittelija omistaa ulkoasun kerran, pohjassa; generaattori vain kaataa tietoja sen kopioihin

Tämä jako skaalautuu pidemmälle, kun uudelleenkäytettävä asettelu sijaitsee omassa työkirjassaan, esimerkiksi raporttien yhteisiä otsake- ja alatunnistepalkkeja sisältävällä laskentataulukolla. CopyRangeTo tekee saman kloonauksen laskentataulukoiden välillä ja ottaa kohdelaskentataulukon sekä kohdekoordinaatit, joten generaattori voi säilyttää yhden koskemattoman pohjalaskentataulukon ja leimata sen alueet niin moneen tuloslaskentataulukkoon kuin työ tarvitsee. Vaihtoehto, pohjan muuttaminen paikallaan ja sen palauttamisen yrittäminen myöhemmin, toimii vain siihen päivään asti, kun ajo keskeytyy puolivälissä

Mitä InsertRows siirtää ja mitä se ei siirrä

Pohjan kasvattamisen malli toimii vain siksi, että XLSX:n InsertRows on rakenteellinen muokkaus eikä solujen sekoitus. Kun se avaa välin, se siirtää lisäyskohdan alapuolella olevat yhdistetyt alueet, rivikorkeudet, hyperlinkit, kommentit, jäädytetyt ruudut, automaattisuodatusalueet, ehdolliset muotoilut, tietojen kelpoisuustarkistukset, taulukot, määritetyt nimet, kuva-ankkurit ja kaavioankkurit, ei vain soluarvoja. Siksi loppusummalohko saapuu uudelle rivilleen yhdistämisineen ja lukumuotoiluineen ehjänä eikä riisuttuna

Sen kaksi dokumentoitua rajoitusta ovat ne, joiden ympärille kannattaa suunnitella. Kaavojen säätö rajoittuu muokattavaan laskentataulukkoon: kyseisen taulukon sisäiset viittaukset kirjoitetaan uudelleen, ja myös toisella laskentataulukolla oleva, siirtyvälle alueelle viittaava kaava kirjoitetaan uudelleen, mutta säätö seuraa vain muokattavaan laskentataulukkoon kohdistuvia viittauksia. Siksi kaikki työkirjojen väliset viittausmallit ansaitsevat oman tarkastuksensa sokon luottamuksen sijaan. Toinen rajoitus on jyrkempi ja koskee XLS-puolta. Pivot-taulukot säilyvät avaa-tallenna-kierroksilla raakoina säilytettyinä tietueina, eivät HotXLS:n siirrettävinä mallinnettuina objekteina, joten rivien lisääminen ei siirrä pivotin aluetta. Kaikissa .xls-muotoon rakennetuissa pohjissa pivot-alueet on pidettävä kaukana kasvavista palkeista

Kieltäydy kirjoittamasta tietoja asettelutilaan

Tuotantoon asti päätyvä yhdistetyn solun virhe ei ole kosmeettinen. Se on rakenteellinen: tietorivi ajautuu yhdistetylle asettelupalkille, sen arvot päätyvät peitettyihin soluihin ja muuttuvat näkymättömiksi, ja sarakesummat lakkaavat hiljaisesti vastaamasta sitä, mitä laskentataulukon lukija näkee. Koska FindAt vastaa minkä tahansa koordinaatin peittävää aluetta koskevaan kysymykseen, generaattori voi kieltäytyä kirjoituksesta heti sen tapahtuessa sen sijaan, että se toimittaisi raportin, joka laskee hiljaa väärin

// kieltäydy kirjoittamasta tietoja yhdistetyn asettelualueen sisään
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;

Sama rajatarkistus kuuluu kaikkialle, missä käyttäjä lajittelee tai suodattaa tulostetta myöhemmin. Aluetta, jonka sisällä on yhdistämisiä, ei voi lajitella luotettavasti, koska lajittelu siirtää rivejä itsenäisesti eikä usean rivin yli ulottuvalla yhdistämisellä ole yhtä riviä, jonka mukana se voisi siirtyä; Excel vastaa virheellä tai sekoittuneella asettelulla. Raportit pysyvät oikeina maantieteellisellä kurilla. Rajaa yhdistämiset otsakepalkkeihin, osioerottimiin ja allekirjoituslohkoihin ja pidä laskentataulukon taulukkomuotoinen keskiosa tasaisena. Raporttipohjien luontia käsittelevä artikkeli kehittää tämän asettelu–data-jaon kokonaiseksi paikkamerkkipohjaiseksi työnkuluksi, ja ehdollista muotoilua ja rich textiä käsittelevä artikkeli käsittelee tämän tasaisen tietopalkin muotoilua

Miten yhdistämiset heikkenevät vietäessä

Yhdistäminen on työkirjakäsite, ja jokainen tekstisuuntautunut vientimuoto noudattaa sitä eri määrin. Kun tunnet kolme toimintatapaa etukäteen, säästät yhden laadunvarmistuskierroksen. HTML-vienti toistaa yhdistämiset uskollisesti ja tuottaa colspan- ja rowspan-attribuutit yhteen taulukkoon, joten selaimelle tarkoitettu raportti säilyttää palkkiasettelunsa. RTF-vienti ei venytä sarakkeita lainkaan: ankkuriteksti sijoittuu omaan soluunsa ja yhdistämisen jäljelle jäävä leveys tulee tyhjinä soluina, jolloin leveä otsikko näyttää tekstinkäsittelyohjelmassa työnnetyltä vasemmalle. CSV:ssä ei ole yhdistämisen käsitettä, joten ankkuriarvo täyttää yhden kentän ja jokainen peitetty solu viedään tyhjänä kenttänä. Jos työkirja tuottaa myös erotinmerkkimuotoisia vientejä, johtopäätös on pitää kaikki kantava sisältö poissa yhdistetystä geometriasta; CSV-, TSV- ja HTML-vientiä käsittelevä artikkeli käy jokaisen muodon yksityiskohtaisesti läpi

HotXLS:n yhdistetty otsikko viety Delphistä HTML:ksi colspan- ja rowspan-arvoilla, RTF:ksi ilman ulottuvuuksia ja CSV:ksi litistettyinä kenttinä
Sama yhdistetty otsikko selviää HTML-viennissä colspan- ja rowspan-kenttien kautta, heikkenee RTF:ssä yksinäiseksi vasemmalle lukituksi soluksi ja litistyy arvoksi plus tyhjiksi kentiksi CSV:ssä

Yksi rauhoittava tieto tiedostokokoa pohtivalle: yhdistämiset maksavat raporttitasolla lähes olemattomasti. Yhdistämistaulukko on solutietoihin verrattuna pieni, ja peitetyn solun lukeminen käyttää edelleen FindAt-hakua skannauksen sijaan. Suurten työkirjojen suorituskykypaine tulee muualta, pääasiassa tyylivarannon kasvusta ja tallennuspolun käyttämästä muistista, joita suurten työkirjojen suorituskykyä käsittelevä artikkeli käsittelee suoraan. Molemmat yhdistämisrajapinnat, rakenteelliset muokkaustoiminnot ja pohjaesittelyt toimitetaan HotXLS Delphi Componentin mukana