Műszaki cikk

XLSX munkalap duplikálása Delphiben a HotXLS segítségével

Pontosan elkészített egy munkalapot. A fejléc összevont, az oszlopszélességek illeszkednek az adatokhoz, a felső két sor rögzített, a nyomtatási terület és a margók a tiszta A4-es exportáláshoz vannak beállítva, a lapfül pedig színes, hogy a pénzügy megtalálja. Most a jelentésnek tizenkettőre van szüksége ezekből, régiónként egyre, mindegyik ugyanabból az elrendezésből kiindulva. Ennek a lapnak a kódban történő tizenkétszeri újraépítése az a mód, ahogyan a finom eltérések beosonnak: a 7. régió egy ponttal szűkebb oszlopot kap, a 11. régió elveszíti a rögzítést, és senki sem veszi észre, amíg a PDF el nem jut az igazgató asztalára. Amire valójában szüksége van, az az Excel jobb gombos kattintás, Áthelyezés vagy másolás, Másolat készítése parancsának programozott verziója: fogja a kész lapot, és hozzon létre független másolatokat

A HotXLS-ben található XLSX motor — egy natív Delphi és C++Builder könyvtár, amely az Excel automatizálása nélkül ír és olvas Excel fájlokat — már képes volt lapokat áthelyezni, törölni, és cellatartományokat másolni a lapok között. Amit a v2.91.0 verzió előtt nem tudott megtenni, az egy teljes munkalap klónozása egyetlen hívással. Ez a kiadás két belépési pontot ad hozzá: a TXLSXWorksheet.CopyFrom metódust, amely átmásolja a lapszintű állapotot az egyik munkalapról a másikra, és a TXLSXSheets.Duplicate metódust, amely hozzáad egy új lapot, és végrehajtja a CopyFrom hívást Ön helyett. Az érdekes rész nem az, hogy dolgokat másol. Hanem a szándékosan meghúzott határvonal aközött, ami mélymásolásra (deep-copy) kerül, és ami nem, valamint az, hogy miért ott van az a határvonal

Egyetlen hívás egy kész lap klónozásához

A magas szintű művelet a Duplicate. Adja át a forráslap 1-alapú indexét, és az egy vadonatúj munkalapot ad vissza, amely tükrözi az eredeti elrendezését és adatait. Az indexelési konvenció megegyezik az XLSX oldalon található Items[] tulajdonsággal, így az első lap indexe 1, nem pedig 0; ha a tartományon kívüli indexet ad meg, akkor kivétel helyett nil-t kap, ami ugyanaz a hibaeset-kezelés, mint amit az XLSX lapgyűjtemény többi része is használ

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;

Két dologra érdemes odafigyelni ebben a részletben. Először is, a FreezePanes a paramétereit sor-elsőként fogadja: FreezePanes(ARow, ACol), így igazodik a Cells[Row, Col] indexeléshez; a másolat örökli a pontos rögzítési felosztást. Másodszor, a metódus neve Duplicate és nem a kézenfekvőbb Copy, és ez nem stílusbeli preferencia. A Copy egy standard eljárás a System egységben, amelyet folyamatosan használnak karakterláncokhoz és dinamikus tömbökhöz. Egy osztályon belüli Copy nevű metódus eltakarná azt a metódustesteken belül, és pontosan olyan feloldási kétértelműséget hozna létre, amely hat hónappal később bosszút állna. A Duplicate megkerüli az egész problémát, és helyesen olvasható a hívás helyén

Az alapértelmezett név az Excel saját szabályát követi

Amikor az egyparaméteres túlterhelést hívja, vagy üres névkarakterláncot ad át, az új lap a forrás után kapja a nevét egy (2) utótaggal, és az utótag addig növekszik, amíg a név egyedivé nem válik. Duplikálja a Template lapot egyszer, és megkapja a Template (2) nevet; duplikálja újra, és megkapja a Template (3) nevet, mivel a Template (2) már foglalt. Ez tükrözi azokat a neveket, amelyeket az Excel generál a saját Másolat készítése parancsából, így a kódja által előállított munkafüzet úgy néz ki, ahogyan a felhasználó elvárná egy kézzel duplikált munkalaptól. Az egyediség ellenőrzése a működő lapgyűjteménnyel szemben fut, ami azt jelenti, hogy átlépi a manuálisan létrehozott neveket is, nem csak a korábbi duplikálásokból származókat

Ha régiónként vagy havonta generál egy lapot, támaszkodjon inkább az explicit névvel ellátott túlterhelésre. Egy jól kiszámítható Region-North, Region-South sémát később könnyebb megcímezni, mint a (2), (3) utótagok sorozatát, és ezáltal a definiált nevek és a lapok közötti képletek is olvashatók maradnak

Mit másol mélyen a CopyFrom

A színfalak mögött a Duplicate hozzáadja a lapot, majd meghívja a CopyFrom(ASource) metódust, amelyet közvetlenül is meghívhat, ha egy már létrehozott lapra szeretne klónozni. A CopyFrom előre véd a két elfajult eset ellen: a nil-ből való másolás vagy egy lap önmagára másolása azonnal visszatér és nem csinál semmit. Minden ezután következő lépés maga a másolás, és ez szándékosan átfogó

A cellaadatok az elsők. A CopyFrom lekéri a forrás UsedRange tulajdonságát, ami a kitöltött cellák és az összevont tartományok szoros befoglaló kerete, és újrahasználja a meglévő CopyRangeTo mechanizmust, hogy minden értéket, képletet és cellánkénti stílusindexet átvigyen a célba az A1 cellától kezdve. A cellákon felül újraalkotja a lapszintű állapotnevek teljes rétegét, amelytől a sablon késznek tűnik:

  • Összevont tartományok, koordináta alapján újra létrehozva, így a fejléc ugyanazt a téglalapot fedi le
  • Oszlopszélességek és sormagasságok, valamint a rejtett, összecsukott és körvonal-szintű listák, szó szerint átmásolva, így a nem alapértelmezett sorok és oszlopok pontosan igazodnak
  • Panelek rögzítése és a nézet állapota: nagyítási szint, rácsvonalak és nulla értékek megjelenítése, jobbról balra irányultság és a nézet típusa
  • Védettségi állapot a műveletenkénti engedélyezési opciókkal, így a zárolt sablon ugyanúgy zárolt marad
  • A teljes oldalbeállítási blokk: margók, tájolás, papírméret, méretezés és oldalhoz igazítás, nyomtatási terület, nyomtatási címek, élőfejek és élőlábak, valamint a nyomtatási rácsvonalak és nyomtatási fejlécek jelzői
  • Az AutoFilter tartomány, a lapfül színe és a lap láthatósága

Az eredmény egy olyan lap, amely a forrásával azonosan nyomtatható, szűrhető és jeleníthető meg. És mivel a cellák, összevonások és méretlisták fizikailag újra létrejönnek az új lapon az alias-képzés helyett, a duplikátum teljesen független. Írjon be 999-et a másolat egyik cellájába, és a forrás megtartja eredeti értékét; ez a függetlenség a legfontosabb tulajdonsága egy párhuzamos regionális jelentésekhez szánt klónnak, és a mellékelt SheetCopy demó ezt kifejezetten megerősíti

Amit sekélyen hagy, és miért

A diagramok, beágyazott képek, XLSX táblázatok, adatérvényesítések és feltételes formázási szabályok nem másolódnak át. Ez egy dokumentált, szándékos határvonal, nem pedig mulasztás, és érdemes megérteni az okát, hogy tervezni tudjon vele, ahelyett, hogy meglepetésként érné

Ezen gyűjtemények mindegyike olyan azonosságot és hivatkozásokat hordoz, amelyek nem élik túl a naiv mezőmásolást. Egy diagram egy forrásadattartományra mutat, és rajzkapcsolattal (drawing relationship) rendelkezik az OOXML csomagban; az objektum klónozása a kapcsolat és a sorozathivatkozások újratérképezése nélkül olyan diagramot eredményez, amely hibás adatok alapján jelenik meg, vagy olyan csomagot, amelyet az Excel javításra szorulóként jelöl meg. Egy táblázatnak olyan névvel kell rendelkeznie, amely egyedi a munkafüzeten belül, egy adott oszlopokhoz kötött fejlécsorral és saját automatikusan generált kapcsolattal rendelkezik. A feltételes formátumok és adatérvényesítések koordinátatartományokhoz kapcsolódnak, és az érvényesítés esetében képlet segítségével más tartományokra is hivatkozhatnak. Ezek bármelyikének helyes mélymásolása a hivatkozások átírását és friss azonosságok kibocsátását jelenti, ami valós hibalehetőségekkel járó, komoly munka. Félig elvégezni, vagyis az objektumot átmásolni a hivatkozásai nélkül, rosszabb, mintha egyáltalán nem másolnánk le: olyan fájlt eredményez, amely javítási felszólítással nyílik meg, és csendben elveti a tartalmat. Ezért a motor azokat a dolgokat másolja át, amelyeket tisztán le tud másolni, a hivatkozásokat tartalmazó gyűjteményeket pedig a hívóra bízza, aki tudja, mire kell a célnak mutatnia

A gyakorlatban ez azt jelenti, hogy egy összetettebb sablon munkafolyamata a következő: duplikálja a lapot a cellák, az elrendezés és a nyomtatási beállítások beszerzéséhez, majd építse újra a diagramot, táblázatot, érvényesítéseket vagy feltételes formázásokat a másolaton ugyanazzal az API-val, amelyet először használt a létrehozásukhoz. Mivel a duplikátum saját tartományaihoz képest hozza létre őket újra, a hivatkozások szerkezetileg helyesek lesznek. Egy A1:C10 tartományt olvasó diagramhoz adjon hozzá egy új diagramot a másolaton, amely a másolat A1:C10 tartományára mutat; ha az AutoFiltert aktívvá szeretné tenni, vegye figyelembe, hogy a szűrési tartomány átmásolódik, így csak az oszlopfeltételeket kell újra alkalmaznia. A feltételes formázási és adatérvényesítési szabályokat ugyanazokon a hívásokon keresztül adhatja hozzá újra, amelyeket az összevont cellákról és a jelentéssablon-elrendezésről szóló cikk ír le, amely végigmegy az összevonási táblán és a tartománymodellen, amelyet a másolat örököl

Hol illeszkedik a duplikálás a jelentési folyamatba

A munkalap duplikálása a helyőrző-alapú generálás természetes kísérője. A Delphiben végzett sablon-alapú jelentésgenerálási útmutatóban található jogkivonat-rögzített (token-anchored) megközelítés megoldja a mások által szerkesztett elrendezésbe történő adatírás problémáját; a duplikálás pedig megoldja azt a problémát, hogy erre az elrendezésre többször van szükség egy munkafüzetben. Kombinálja őket, és a minta tiszta: tartson meg egy eredeti Template lapot a tokenjeivel, összevonásaival és nyomtatási beállításaival, majd minden régió vagy időszak esetében hívja meg a Duplicate metódust, töltse ki a klón tokenjeit az adott adatszelettel, és lépjen tovább. Az eredeti sablon soha nem módosul, így megbízható forrás marad a következő klón számára, és minden kimeneti lap bájt-pontosan megegyező elrendezésből indul ki

Egy sorrendi megjegyzés megelőzheti a félreértéseket. A lapot az adatok betöltése előtt duplikálja, ne utána. A sablonnak a szerkezetet és a formázást kell tartalmaznia, nem az előző negyedév számait; egy üres, formázott lap klónozása azt jelenti, hogy minden másolat tisztán indul. Ha olyan lapot duplikál, amely már tartalmaz adatokat, az adatok is átkerülnek, mert a CopyFrom hűen másolja a használt tartományt; ez időnként kívánatos lehet, de a szétágazó jelentéseknél általában nem az

Gyors ellenőrzési rutin

Mivel a mélymásolás és a sekélymásolás közötti különbség láthatatlan, amíg nem keresi, építsen be egy ötsoros ellenőrzést a folyamatba, ahelyett, hogy bízna abban, hogy minden átment. A duplikálás után olvassa vissza azokat a strukturális jeleket, amelyeket a klónoknak örökölniük kellene, és ellenőrizze, hogy megegyeznek-e a forrással

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)]));
// Függetlenség bizonyítása: módosítjuk a másolatot, ellenőrizzük, hogy a forrás érintetlen maradt.
Copy.Cells[2, 2].Value := 999;
// A Template.Cells[2, 2].Value továbbra is az, ami volt.

Az összevonások száma, az oszlop szélessége, a rögzített sor és a lapfül színének jelzője jelzi, hogy a másolt réteg valóban megérkezett. Különállóan, minden olyan lapon, amely diagramot, táblázatot, adatérvényesítést vagy feltételes formázást tartalmazott, kezelje ezeket újraépítendő listaként a másolaton: hiányuk tervezési döntés eredménye, és a javításuk néhány hívással megoldható, nem igényel hibabejelentést. Ez a mentális modell — mély ott, ahol biztonságos, és sekély ott, ahol a hivatkozások megszakadnának — a kulcsa ezen funkció jó használatának

Az itt leírt munkalap-duplikálás és a CopyFrom lapszintű másolás a natív HotXLS Delphi táblázatkezelő komponens v2.91.0-s verziójában érhető el, a futtatható SheetCopy mintával együtt, amely a klónozási és módosítási ciklust gyakorolja tetőtől talpig