Műszaki cikk

HotXLS diagramok és képek: Excel rajzobjektumok Delphiben

Bármi, ami a munkalap rácsa fölött lebeg (egy diagram, egy logó, egy bélyegző, egy magyarázódoboz), rajzobjektum, és egy rajzobjektumot két dolog határoz meg: hogy mi az, és hogy hová van lehorgonyozva. A horgony az a rész, amelyet a fejlesztők elrontanak. A diagram nem egy cellában lakik; egy téglalapban ül, amelyet sorok és oszlopok egy szakaszához tűztek, az általa ábrázolt adat pedig abszolút A1-hivatkozások külön halmaza, amelyről a horgony semmit nem tud. Mozgassa el a keretet, és az ábrázolás a helyén marad. Szúrjon be alá sorokat, és a keret velük együtt csúszik lejjebb. E két koordináta-rendszer tisztán tartása adja a rajzoló kód helyes viselkedésének nagy részét

A HotXLS natív Object Pascal könyvtár, amely Excel-automatizálás nélkül olvas és ír XLS és XLSX fájlokat, és két külön rajzmodellt hordoz, mert a két fájlformátum eltérően tárolja a rajzokat. A BIFF8 .xls formátum a diagramokat saját dedikált lapokon tartja, a lebegő alakzatokat pedig a munkalaphoz csatolt OfficeArt adatfolyamban. Az OOXML .xlsx formátum a diagramot beágyazhatja magába a rácsba, egy cellatéglalaphoz horgonyozva, ugyanolyan lebegő képek és alakzatok mellett. Az objektummodell ezt a kettéválást tükrözi, és a megírásra érdemes hibák mind abból származnak, hogy valaki az egyik formátum szabályait alkalmazza a másikra

Melyik tároló mit tud tartani

A tároló megválasztásának minden diagramkód előtt meg kell történnie, mert az elérhető objektumtípusok eltérnek a kettő között:

  • XLS (BIFF8): a diagramok dedikált diagramlapokon élnek, amelyeket a Sheets gyűjtemény AddChartSheet metódusával hoz létre. A képek, szövegdobozok, téglalapok, ellipszisek és vonalak OfficeArt alakzatok, amelyeket a munkalap Shapes gyűjteménye kezel. Nincs API arra, hogy diagramot ágyazzon be egy közönséges munkalap rácsába
  • XLSX (OOXML): a diagramok közvetlenül beágyazhatók egy munkalapba a TXLSXWorksheet.AddChart hívással, cellatéglalaphoz horgonyozva, vagy elhelyezhetők dedikált diagramlapon a TXLSXWorkbook.AddChartSheet hívással. A képek az AddImage vagy az AddImageFromFile metódussal kerülnek be, a lebegő feliratok pedig az AddTextBox hívással
A HotXLS rajztárolóit Delphiből összehasonlító ábra: diagramlapok és OfficeArt alakzatok a régi XLS formátumban, szemben a beágyazott diagramokkal, képekkel és szövegdobozokkal az XLSX formátumban
A két fájlformátum különböző rajz-API-kat tesz elérhetővé, ezért a tárolót minden diagramkód megírása előtt ki kell választani

Így egy „olyan irányítópultlap, amelyen a diagram a számok mellett van” megfogalmazású követelmény valójában .xlsx formátumot követel meg. Ezt .xls fájlban csak úgy tudja megközelíteni, hogy a diagramot saját lapra tolja, ami megváltoztatja, hogyan navigál a felhasználó a fájlban, és azt is, hogyan kell a kódjának viselkednie. Az XLS oldali AddChartSheet által visszaadott lap diagram-alfolyam, nem rács: ha a Cells.Item hívással ír bele, inkonzisztens rajzfolyamot állít elő, amely hiba nélkül generálódik, majd az Excel megnyitáskor eldobja. A diagram egyszerűen eltűnik, és a fordítási naplóban semmi nem mondja meg, miért. Kezelje a visszaadott lapot kizárólag diagramként, és a „hiányzó diagram” hibabejelentések egész osztálya megszűnik

Diagram beágyazása XLSX munkalapba

Az XLSX útvonal az, ahol van mozgástér, és itt válik kézzelfoghatóvá a bevezetőben említett két koordináta-rendszer. Az AddChart hívásnak átadott horgonytéglalap a munkalap sorai és oszlopai szerint van kifejezve, és azt rögzíti, hol ül a diagram kerete. Az adatsorok abszolút A1-hivatkozásokként vannak kifejezve, amelyek tartalmazzák a lap nevét. A kettő független: a keretet átteheti a lap túlsó felére, és attól még ugyanazokat a cellákat ábrázolja

A HotXLS TXLSXWorksheet.AddChart hívása mögötti két koordináta-rendszert bemutató ábra: a diagram kerete a munkalap soraihoz és oszlopaihoz horgonyozva, míg az adatsorai abszolút A1-hivatkozásokat használnak
A keret sorokhoz és oszlopokhoz van tűzve, míg az ábrázolás abszolút A1-hivatkozásokat olvas, és egyik koordináta-rendszer sem tud a másikról
var
  Book: TXLSXWorkbook;
  Sheet: TXLSXWorksheet;
  Chart: TXLSXChart;
begin
  Book := TXLSXWorkbook.Create;
  try
    Sheet := Book.Sheets.Add('Sales');
    Sheet.Cells[1, 1].Value := 'Region';
    Sheet.Cells[1, 2].Value := 'Revenue';
    Sheet.Cells[2, 1].Value := 'East';
    Sheet.Cells[2, 2].Value := 1184350;
    Sheet.Cells[3, 1].Value := 'Central';
    Sheet.Cells[3, 2].Value := 902210;
    Sheet.Cells[4, 1].Value := 'West';
    Sheet.Cells[4, 2].Value := 1010675;

    // A keret a 6..22 sorokhoz és az 1..8 oszlopokhoz horgonyozva
    Chart := Sheet.AddChart(xlsxChartColumn, 'Revenue by Region', 6, 1, 22, 8);
    Chart.AddSeries('Revenue', 'Sales!$A$2:$A$4', 'Sales!$B$2:$B$4');
    Chart.ValueAxisTitle := 'USD';

    Sheet.AddImageFromFile(1, 5, 'logo.png');
    Book.SaveAs('dashboard.xlsx');
  finally
    Book.Free;
  end;
end;

Az az argumentum harap, amelyet az AddSeries hívásnak átadott tartomány-karakterlánc jelent. Ez literál, amely a hívás pillanatában rögzül, és fogalma sincs róla, hogy Ön később még húsz sornyi adatot fűzhet hozzá. Az adat kiírása után kiszámolt sorszámból építse fel, soha nem előtte. A pont- és buborékdiagramok ugyanezt a két argumentumot más jelentéssel terhelik meg: a kategóriatartomány most az X értékeket szolgáltatja, az értéktartomány pedig az Y értékeket, a buborék sugara pedig egy harmadik hivatkozásból származik, amelyet a visszaadott TXLSXChartSeries objektum BubbleSizeRange tulajdonságán állít be. Amint elhagyja az oszlop- és sávdiagramok családját, olvassa a hívást „X, Y, méret”, ne pedig „kategóriák, értékek” alakban

A TXLSXChartType lefedi az oszlop-, sáv-, vonal-, kör-, terület-, gyűrű-, pont-, buborék- és sugárdiagramokat, ami kiszolgálja a mindennapi jelentéskészítés repertoárját. Egy teljes oldalas, környező rács nélküli diagramhoz a Book.AddChartSheet olyan lapot ad vissza, amelynek IsChartSheet tulajdonsága igaz. Ez a régi diagramlap .xlsx megfelelője, és ugyanaz az elvárás vonatkozik rá: ne írjon bele cellatartalmat

A képek bájtként kerülnek be, és EMU-ban vannak méretezve

Két túlterhelt változat áll rendelkezésre kép beszúrására, és összekeverésük az a képhiba, amely a leggyakrabban bukkan fel kódellenőrzésen. Az AddImage(ARow, ACol, AData, AFormat) a már kódolt képbájtokat várja az AData paraméterben: egy PNG, JPEG, GIF vagy BMP nyers tartalmát. Adjon át neki fájlútvonalat, és egy negyven bájtos karakterláncot tárolt el, amelyet egyetlen megjelenítő sem tud dekódolni — pontosan az a törött képikonos hibabejelentés, amelyet üzembe helyezés után nem szeretne hibakeresni. Ha a forrás lemezen lévő fájl, hívja helyette az AddImageFromFile metódust, és hagyja, hogy a könyvtár olvassa be a bájtokat, és határozza meg a formátumot

Ezután jön a méretezés. A DrawingML nem képpontban mér, hanem angol metrikus egységben, ahol 914400 EMU tesz ki egy hüvelyket, és 96 DPI mellett 9525 EMU egy képpontot. A TXLSXImage objektum a WidthEMU és a HeightEMU tulajdonságot teszi elérhetővé, tehát egy 180-szor 60 képpontosra szánt logóhoz 1714500-szor 571500 EMU kell. Tegye ezt az átváltást elnevezett konstansba, és azzal számoljon. A kódban szétszórt bűvös számok, mint az 1714500, olvashatatlanok, és csendben hibássá válnak, amint valaki megváltoztatja a cél-DPI értéket. A horgony sora és oszlopa egyébként 1-alapú, illeszkedve a cella-API többi részéhez, nem pedig a 0-alapú EMU-matematikához

A HotXLS képbeszúrását Delphiből bemutató ábra: az AddImage kódolt bájtokat vár, míg az AddImageFromFile beolvassa a fájlt, és a 96 DPI-s képpontok WidthEMU és HeightEMU értékekké alakulnak
A képbájtok és a fájlútvonalak külön túlterhelt változatokhoz tartoznak, a képernyőn megjelenő képpontméretek pedig EMU-ra váltódnak át, mielőtt elérnék a képobjektumot

Diagramlapok és alakzatok a régi XLS fájlokban

A BIFF8 oldalon az AddChartSheet gazdagabb túlterhelt változata a diagram típusát, a tengelycímeket és TXLSChartSeriesInfo rekordok nyílt tömbjét várja, amelyek mindegyike egy nevet, valamint egy kategória- és egy értéktartományt hordoz karakterláncként. A lebegő alakzatok külön ügyet jelentenek: magára az adatmunkalapra kerülnek, annak Shapes gyűjteményén keresztül, nem a diagramlapra

var
  Book: IXLSWorkbook;
  Data, Trend: IXLSWorksheet;
  Series: array[0..0] of TXLSChartSeriesInfo;
begin
  Book := TXLSWorkbook.Create;   // interfészszámlált: ne hívjon rá Free-t
  Data := Book.Sheets.Add;
  Data.Name := 'Data';
  Data.Cells.Item[1, 1].Value := 'Month';
  Data.Cells.Item[1, 2].Value := 'Units';
  Data.Cells.Item[2, 1].Value := 'Apr';
  Data.Cells.Item[2, 2].Value := 1530;
  Data.Cells.Item[3, 1].Value := 'May';
  Data.Cells.Item[3, 2].Value := 1721;

  Series[0].Name := 'Units';
  Series[0].Categories := 'Data!$A$2:$A$3';
  Series[0].Values := 'Data!$B$2:$B$3';
  Trend := Book.Sheets.AddChartSheet('Trend', xlsChartTypeLine,
    'Units sold', 'Month', 'Units', Series);
  // A Trend diagram-alfolyam: soha ne hívjon rajta cellametódusokat

  Data.Shapes.AddTextBox('Source: ERP nightly export', 6, 1, 8, 4);
  Data.Shapes.AddPicture('approved-stamp.bmp');
  Book.SaveAs('trend.xls');
end;

Két élettartam-részlet számít itt, és ellenkező irányba húznak. A TXLSWorkbook objektumot az IXLSWorkbook interfészen keresztül tartja a kód, és referenciaszámlált, tehát ha Ön maga hívja rajta a Free metódust, dupla felszabadítást vált ki. Az előző szakaszok TXLSXWorkbook objektuma közönséges objektum, és try..finally blokkban kell felszabadítani. Ugyanannak a kódellenőrnek, aki az XLSX oldalon a hiányzó Free hívást kifogásolja, az XLS oldalon a meglévőt kell kifogásolnia, ami valódi botlásveszély, amikor ugyanabban a unitban mindkét formátummal dolgozik. Maguk az alakzatsegédek egységesek: az AddRectangle, az AddOval és az AddLine, valamint a DeleteInRange egy régió rajzainak törlésére, mind sor- és oszloppárok szerint horgonyoznak, így az a sablon, amely fölébük sorokat szúr be, a ráccsal együtt tolja el őket

Még egy tulajdonság megszolgálja a helyét a régi fájloknál. A TXLSPicture.TransparentColor kimaszkol egy kiválasztott háttérszínt a bitképből, és így ejt egy nem téglalap alakú bélyegzőt (egy „Approved” pecsétet, egy vízjelet) a rács fölé olyan formátumban, amelynek BIFF-renderelése sosem tanulta meg a PNG-alfát. Állítsa be azt a színt, amelyre a bélyegzőt tervezték, és a körülötte lévő téglalap eltűnik

A témaszínek nem élik túl a BIFF8 körutat

Az OOXML rajzkitöltései mutathatnak egy témaszín-helyre, és ezért olcsó egy egész .xlsx átszínezése a témája lecserélésével. A BIFF8 rajzrekordjaiban nincs ilyen hely. Amikor a HotXLS témaszínt alkalmaz egy XLS rajzra, a színt konkrét RGB értékké oldja fel, és azt tárolja; az a témaindex, amelyből származott, a fájl kiírásának pillanatában elvész, és az újranyitás sem tudja visszaszerezni. Ez különösen a fehércímkés jelentéskészítő eszközöket éri utol — azokat, amelyek ugyanazt a generált dokumentumot sok ügyfélre márkázzák át. Tartsa a téma–RGB leképezést a saját konfigurációjában, és minden generáláskor alkalmazza újra, ahelyett, hogy egy mentett .xls fájlból várná vissza olvasni

Egy kapcsolódó döntés a teljesítmény oldalán jelentkezik. Az XLS homlokzatnak megmondható, hogy teljesen hagyja ki a rajzréteg elemzését, amikor egy nagy örökölt fájlból csak a cellaadatokra van szüksége: ehhez a _DisableGraphics értéket kell igazra állítani, és ez valódi időt farag le a tömeges olvasásokból. A hátulütő végleges: az így megnyitott munkafüzetnek nincs OfficeArt adatfolyama a memóriában, tehát a mentése a rajzokat kiírja a világból. Tartsa fenn ezt a jelzőt csak olvasható elemzési feladatokra. A tágabb teljesítményképet a HotXLS nagy munkafüzetek teljesítményéről szóló jegyzeteink tárgyalják

Hogyan tartsa stabilan a horgonyokat, miközben a rács változik

A jelentések ritkán maradnak akkorák, amekkorára generálták őket, és itt térül meg a bevezetőben leírt horgonymodell. Az XLSX homlokzat szerkezeti műveletei (az InsertRows, a DeleteRows és az oszlopos megfelelőik) a cellákkal együtt mozgatják a függő rétegeket is. Az egyesített régiók, a hiperhivatkozások, a megjegyzések, a rögzített panelek, a szűrőtartományok, a feltételes formátumok, az érvényesítések, a táblázatok, a definiált nevek és — e téma szempontjából — a kép- és diagramhorgonyok mind együtt utaznak. Az 1. sorhoz horgonyzott logó a tetején marad, amikor alatta tíz sor kerül be. Az adatblokk alá horgonyzott diagramkeret lecsúszik, ahogy a blokk nő. Az az egy dolog, ami nem íródik át, minden olyan tartomány-karakterlánc, amelyet literálként rögzített a beszúrás előtt, hiszen az csak szöveg, amelyet a könyvtárnak semmi oka újra megnézni. Ez rögzíti a sablonkitöltés biztonságos sorrendjét: először írja meg és alakítsa át az adatot, a diagramok létrehozása és a képek elhelyezése pedig legyen az utolsó menet, és minden tartomány-karakterláncot a beszúrások utáni sorszámokból származtasson, ne az előttiekből

Két kisebb eszköz teszi teljessé az elhelyezési készletet. Az XLS oldali TXLSTextBox.SetArea egy meglévő szövegdobozt vagy automatikus alakzatot horgonyoz át új cellatéglalapra, ami jobb, mint törölni és újra létrehozni, amikor egy lábléc-blokk elcsúszik. Az AddPicture bitképes túlterhelt változata pedig élő TBitmap objektumot vár egy opcionális átlátszósági jelzővel, így bármi, amit a saját VCL kódja meg tud rajzolni (egy műszer, egy sparkline-sáv, egy diagramtípus, amelyet a natív lista nem kínál), közvetlenül a lapra bélyegezhető anélkül, hogy előbb ideiglenes fájlt kellene írni

A diagramok és a képek szinte mindig a záró réteget jelentik egy már strukturált jelentésen, és éppen ezért az alapozás dönti el, hogy tisztán érkeznek-e a helyükre. Annak az adatnak a kitöltését, amelyre a diagram hivatkozni fog, a sablonvezérelt jelentésgenerálás tárgyalja, a rács stabilan tartása a horgonyai alatt pedig az egyesített cellák és elrendezésvezérlés témája. A teljes osztály- és metódusdokumentáció a HotXLS Delphi Component termékoldalán található