Techninis straipsnis

XLSX darbinio lapo dubliavimas Delphi aplinkoje su HotXLS

Sukurkite vieną darbinį lapą tiksliai taip, kaip reikia. Antraštės juosta yra sujungta, stulpelių pločiai atitinka duomenis, dvi viršutinės eilutės yra užfiksuotos, spausdinimo sritis bei paraštės nustatytos švariam A4 formato eksportui, o kortelė nuspalvinta taip, kad finansų skyrius galėtų ją rasti. Dabar ataskaitai reikia dvylikos tokių lapų – po vieną kiekvienam regionui, pradedant nuo to paties maketo. Šio lapo perstatymas kode dvylika kartų yra būdas, kaip atsiranda nedideli nukrypimai: regionas 7 gauna vienu punktu siauresnį stulpelį, regionas 11 praranda fiksavimą, ir niekas to nepastebi, kol PDF neatsiduria ant vadovo stalo. Tai, ko jums iš tikrųjų reikia, yra programinė „Excel“ dešiniojo pelės klavišo spustelėjimo komandos „Move or Copy“ (Perkelti arba kopijuoti) ir „Create a copy“ (Sukurti kopiją) versija: paimti paruoštą lapą ir sukurti nepriklausomus dublikatus

„HotXLS“ – vietinė Delphi ir C++Builder biblioteka, kuri skaito ir rašo „Excel“ failus nenaudodama pačios „Excel“ automatizavimo – XLSX variklis jau galėjo perkelti lapus, trinti lapus ir kopijuoti langelių diapazonus tarp lapų. Tai, ko jis negalėjo padaryti iki versijos v2.91.0, buvo viso darbinio lapo klonavimas vienu iškvietimu. Šis leidimas prideda du įėjimo taškus: TXLSXWorksheet.CopyFrom, kuris nukopijuoja lapo lygio būseną iš vieno darbinio lapo į kitą, ir TXLSXSheets.Duplicate, kuris prideda naują lapą ir paleidžia CopyFrom už jus. Įdomiausia dalis yra ne tai, kad jis nukopijuoja dalykus. Tai yra sąmoninga riba tarp to, kas kopijuojama giliai (deep-copy), ir to, kas ne, bei kodėl ši riba yra būtent ten

Vienas iškvietimas paruoštam lapui klonuoti

Aukšto lygio operacija yra Duplicate. Pateikite jai šaltinio lapo indeksą (skaičiuojamą nuo 1) ir ji grąžins visiškai naują darbinį lapą, kuris atspindi originalo maketą ir duomenis. Indeksų taisyklė atitinka Items[] XLSX pusėje, todėl pirmasis lapas yra indeksas 1, o ne 0; perdavus indeksą už ribų, gausite nil, o ne išimtį (exception) – tai yra ta pati klaidų sutartis, kurią naudoja likusi XLSX lapų kolekcija

var
  Book: TXLSXWorkbook;
  Template, Copy: TXLSXWorksheet;
begin
  Book := TXLSXWorkbook.Create;
  try
    Template := Book.Sheets.Add('Template');
    Template.Cells[1, 1].Value := 'Quarterly Statement';
    Template.Range['A1:C1'].Merge;
    Template.ColWidth[1] := 18;
    Template.FreezePanes(2, 1);          // freeze top row + first column
    Template.TabColorIsAuto := False;
    Template.TabColor := $FF1F4E79;

    // Clone with an explicit name...
    Copy := Book.Sheets.Duplicate(1, 'Region-North');
    // ...or let it pick the Excel-style default name.
    Copy := Book.Sheets.Duplicate(1);    // -> "Template (2)"

    Book.SaveAs('regions.xlsx');
  finally
    Book.Free;
  end;
end;

Verta atkreipti dėmesį į du dalykus šiame kodo fragmente. Pirma, FreezePanes priima argumentus pradedant nuo eilutės, t. y. FreezePanes(ARow, ACol), todėl tai sutampa su Cells[Row, Col] indeksavimu; dublikatas paveldi tikslų fiksavimo padalijimą. Antra, metodas pavadintas Duplicate, o ne labiau akivaizdžiu Copy, ir tai nėra stiliaus pasirinkimas. Copy yra standartinė procedūra System modulyje, nuolat naudojama eilutėms ir dinaminiams masyvams. Metodas, pavadintas Copy klasėje, užgožtų ją metodų viduje ir sukurtų būtent tokį dviprasmiškumą, kuris sukeltų problemų po pusmečio. Duplicate leidžia išvengti šios problemos ir atrodo teisingai iškvietimo vietoje

Numatytasis pavadinimas seka pačios „Excel“ taisykle

Kai iškviečiate perkrovą su vienu argumentu arba perduodate tuščią pavadinimo eilutę, naujas lapas pavadinamas pagal šaltinį su priesaga (2), o priesaga didinama, kol pavadinimas tampa unikalus. Padubliuokite lapą Template vieną kartą ir gausite Template (2); padubliuokite dar kartą ir gausite Template (3), nes Template (2) jau užimtas. Tai atspindi pavadinimus, kuriuos „Excel“ generuoja iš savo komandos „Sukurti kopiją“, todėl jūsų kodo sukurta darbo knyga atrodo taip, kaip vartotojas tikisi matyti rankiniu būdu nukopijuotą knygą. Unikalumo patikra vykdoma pagal esamą lapų kolekciją, zodžiu, tai reiškia, kad ji taip pat praleidžia rankiniu būdu sukurtus pavadinimus, o ne tik tuos, kurie atsirado iš ankstesnių dubliavimų

Jei generuojate po vieną lapą regionui ar mėnesiui, geriau naudokite perkrovą su aiškiu pavadinimu. Prognozuojama Region-North, Region-South schema yra lengviau valdoma vėliau nei (2), (3) priesagų eilutė, be to, tai išlaiko jūsų apibrėžtus pavadinimus bei kryžmines formules įskaitomas

Ką giliai kopijuoja „CopyFrom“

Po variklio gaubtu Duplicate prideda lapą ir tada iškviečia CopyFrom(ASource), kurį taip pat galite iškviesti tiesiogiai, kai norite klonuoti būseną ant jau sukurto lapo. CopyFrom iš anksto apsaugo nuo dviejų išskirtinių atvejų: kopijavimo iš nil arba lapo kopijavimo ant paties savęs – abu iškart sugrąžina valdymą ir nieko nedaro. Viskas po to yra pats kopijavimas, ir jis yra sąmoningai platus

Pirmiausia perkeliama langelių informacija. CopyFrom paprašo šaltinio jo UsedRange – užpildytų langelių ir sujungtų sričių ribojančio rėmelio – ir pakartotinai naudoja esamą CopyRangeTo mechanizmą, kad perkeltų kiekvieną reikšmę, formulę bei langelio stiliaus indeksą į tikslinį lapą, pradedant nuo A1. Be to, ji atkuria visą lapo lygio būsenos sluoksnį, kuris suteikia šablonui užbaigtą išvaizdą:

  • Sujungti diapazonai, sukurti iš naujo pagal koordinates, kad skydelis apimtų tą patį stačiakampį
  • Stulpelių pločiai ir eilučių aukščiai, taip pat paslėptų, sutrauktų bei kontūro lygio sąrašai, nukopijuoti pažodžiui, kad nestandartinės eilutės ir stulpeliai išsilygiuotų tiksliai
  • Užfiksuotos sritys (panes) ir rodinio būsena: mastelis, tinklelio ir nulinių reikšmių rodymas, kryptis iš dešinės į kairę bei rodinio tipas
  • Apsaugos būsena su leidimų parinktimis kiekvienam veiksmui, kad užrakintas šablonas liktų užrakintas taip pat
  • Visas puslapio nustatymų blokas: paraštės, orientacija, popieriaus dydis, mastelio keitimas ir pritaikymas puslapiui, spausdinimo sritis, spausdinimo antraštės, viršutinės bei apatinės paraštės antraštės ir spausdinimo tinklelio bei spausdinimo antraščių vėliavos
  • Automatinio filtro diapazonas, kortelės spalva ir lapo matomumas

Rezultatas yra lapas, kuris spausdinamas, filtruojamas ir pateikiamas identiškai jo šaltiniui. Kadangi langeliai, sujungimai ir matmenų sąrašai yra fiziškai sukuriami naujame lape, o ne tiesiog nurodomi kaip alternatyvus pavadinimas (aliased), dublikatas yra visiškai nepriklausomas. Įrašykite 999 į kopijos langelį ir šaltinis išlaikys savo pradinę reikšmę; šis nepriklausomumas yra svarbiausia klono savybė, skirta lygiagrečioms regioninėms ataskaitoms, ir pateiktas SheetCopy demonstracinis pavyzdys tai patvirtina aiškiai

Ką ji palieka sekliai ir kodėl

Dabar atvira dalis. Diagramos, įterpti paveikslėliai, XLSX lentelės, duomenų patvirtinimai (data validations) ir sąlyginio formatavimo taisyklės yra nekopijuojamos. Tai yra dokumentuota, sąmoninga riba, o ne priežiūros klaida, ir verta suprasti jos priežastis, kad galėtumėte planuoti darbus, o ne nustebti

Kiekviena iš šių kolekcijų turi tapatybę ir nuorodas, kurios neišlieka po paprasto laukų kopijavimo. Diagrama rodo į šaltinio duomenų diapazoną ir turi piešimo ryšį (relationship) OOXML pakete; klonuojant objektą be ryšio bei serijų nuorodų perrašymo, gaunama diagrama, kuri atvaizduoja neteisingus duomenis, arba paketas, kurį „Excel“ pažymi kaip reikalaujantį taisymo. Lentelė turi pavadinimą, kuris turi būti unikalus darbo knygoje, antraščių eilutę, susietą su konkrečiais stulpeliais, ir savo automatiškai sugeneruotą ryšį. Sąlyginiai formatai ir duomenų patvirtinimai prisiriša prie koordinačių diapazonų ir, patvirtinimo atveju, gali nurodyti kitus diapazonus per formulę. Gilus bet kurio iš šių dalykų kopijavimas reiškia nuorodų perrašymą ir naujų tapatybių suteikimą, kas yra realus darbas su realiais gedimų režimais. Daryti tai pusiau – kopijuojant objektą, bet nuorodų neperrašant – yra blogiau nei nekopijuoti išvis: gaunamas failas, kuris atsidaro su pranešimu apie taisymą ir tyliai atmeta turinį. Todėl variklis nukopijuoja tai, ką gali nukopijuoti švariai, o nuorodas turinčias kolekcijas palieka kviečiančiajam, kuris žino, į ką turėtų rodyti tikslas

Praktiškai tai reiškia, kad sudėtingesnio šablono darbo eiga yra tokia: padubliuokite lapą, kad gautumėte langelius, maketą ir spausdinimo nustatymus, tada atkurkite diagramą, lentelę, patvirtinimus ar sąlyginius formatus kopijoje, naudodami tą pačią API, kurią naudojote juos kurdami pirmą kartą. Kadangi juos atkuriate pagal pačios kopijos diapazonus, nuorodos dėl konstrukcijos gaunamos teisingos. Diagramai, kuri skaito A1:C10, pridėkite naują diagramą kopijoje, rodančią į kopijos A1:C10; jei norite automatinio filtro, atkreipkite dėmesį, kad filtro diapazonas persikelia, todėl jums tereikia vėl pritaikyti stulpelių kriterijus. Sąlyginio formatavimo bei duomenų patvirtinimo taisykles pridėtumėte iš naujo naudodami tuos pačius iškvietimus, aprašytus straipsnyje apie sujungtus langelius ir ataskaitų šablonų maketavimą, kuris aprašo sujungimų lentelę ir diapazonų modelį, kurį paveldi kopija

Kur dubliavimas tinka ataskaitų generavimo srautui

Darbinio lapo dubliavimas yra natūralus kompanionas generavimui naudojant vietos rezervavimo ženklus (placeholders). Žymomis pagrįstas metodas, aprašytas šablonais pagrįsto ataskaitų generavimo Delphi vadove, išsprendžia duomenų rašymo į kitų žmonių redaguojamą maketą problemą; dubliavimas išsprendžia poreikį turėti šį maketą daug kartų vienoje darbo knygoje. Sujunkite juos ir gausite švarų šabloną: laikykite vieną nepaliestą Template lapą su jo žymomis, sujungimais bei spausdinimo nustatymais, o tada kiekvienam regionui ar laikotarpiui iškvieskite Duplicate, užpildykite klono žymes tų duomenų dalimi ir judėkite toliau. Nepaliestas šablonas niekada nemutuojamas, todėl išlieka patikimas šaltinis kitam klonui, o kiekvienas išvesties lapas prasideda nuo baitas po baito identiško maketo

Viena pastaba apie eiliškumą padeda išvengti painiavos. Padubliuokite lapą prieš supildami į jį duomenis, o ne po to. Šablonas turėtų turėti struktūrą bei formatavimą, o ne praėjusio ketvirčio skaičius. Klonuojant tuščią stilizuotą lapą, kiekvienas dublikatas prasideda švarus. Jei padubliuosite lapą, kuriame jau yra duomenų, tie duomenys persikels, nes CopyFrom ištikimai nukopijuoja panaudotą diapazoną; kartais to norima, tačiau pasiskirstančioms ataskaitoms (fan-out reports) paprastai tai netinka

Greitas tikrinimo įprotis

Kadangi giliojo ir sekliojo kopijavimo skirtumas yra nematomas, kol jo neieškote, sukurkite penkių eilučių patikrą savo darbe, užuot tiesiog tikėjęsi, kad viskas persikėlė. Padubliavę nustatykite struktūrinius signalus, kuriuos klonas turėtų paveldėti, ir įsitikinkite, kad jie atitinka šaltinį

Copy := Book.Sheets.Duplicate(1, 'Region-North');
WriteLn(Format('merged=%d  colA=%.1f  freezeRow=%d  tabAuto=%d',
  [Copy.MergedCells.Count, Copy.ColWidth[1],
   Copy.FreezeRow, Integer(Copy.TabColorIsAuto)]));
// Prove independence: mutate the copy, confirm the source is untouched.
Copy.Cells[2, 2].Value := 999;
// Template.Cells[2, 2].Value is still whatever it was.

Sujungimų skaičius, stulpelio plotis, užfiksuota eilutė ir kortelės spalvos vėliava rodo, kad nukopijuotas sluoksnis iš tikrųjų buvo perkeltas. Be to, bet kuriame lape, kuriame buvo diagrama, lentelė, patvirtinimai ar sąlyginiai formatai, traktuokite juos kaip atkuriamų elementų sąrašą kopijoje: jų nebuvimas yra numatytas, o sprendimas yra keli iškvietimai, o ne pranešimas apie klaidą. Šis mentalinis modelis – gilus ten, kur saugu, ir seklys ten, kur nuorodos sugestų – yra visa istorija, kaip gerai naudotis šia funkcija

Darbinio lapo dubliavimas ir CopyFrom lapo būsenos kopijavimas, aprašyti čia, pateikiami versijoje v2.91.0 vietiniame „HotXLS Delphi spreadsheet component“ pakete kartu su paleidžiamu SheetCopy pavyzdžiu, kuris demonstruoja visą klonavimo ir mutavimo ciklą nuo pradžios iki galo