Technisch artikel

Excel-sparklines in Delphi: HotXLS en de x14-extensie

HotXLS maakt Excel-sparklines — de minigrafieken die Excel in een enkele cel tekent — vanuit Delphi en C++Builder met één aanroep: TXLSXWorksheet.AddSparklineGroup bouwt een sparklinegroep over een gegevensbereik, en het opslaan van het werkboek schrijft dit als een x14-extensieblok in de extLst van het werkblad, die Excel 2010 en nieuwer renderen en oudere lezers zonder problemen overslaan. Geen grafiekobjecten, geen tekeningonderdelen, geen ankers

Het scenario dat sparklines de moeite waard maakt, is altijd hetzelfde. Een dashboard-werkboek bevat een paar honderd rijen, één per product, regio of account, met elk twaalf maandelijkse cijfers, en iemand wil een trend-symbool aan het einde van elke rij. Als u dat modelleert met echte grafieken, maakt u honderden DrawingML-grafiekonderdelen aan, elk met een eigen XML-onderdeel, relatie en ankergeometrie, om nog maar te zwijgen over wat dat doet met de bestandsgrootte en de opentijd. Een sparklinegroep is één XML-blok dat zegt: "teken een piepkleine lijn van B2:M2 in N2, en van B3:M3 in N3," goedkoop herhaald voor zoveel rijen als u hebt

Waarom zijn sparklines een x14-extensie in plaats van een grafiek?

Sparklines kwamen met Excel 2010, drie jaar nadat het ECMA-376-basisschema was bevroren, dus ze konden niet in de eigenlijke werkbladmarkup staan zonder elke bestaande lezer te breken. Het antwoord van Microsoft is het mechanisme van de extensielijst: een werkblad kan eindigen met een <extLst>-element dat <ext>-blokken bevat, elk getagd met een URI die de functie identificeert ([MS-XLSX] §2.3.6). Sparklines gebruiken de URI {05C60535-1F16-4fd2-B633-F4F36F0B64E0} and de x14-naamruimte. Een lezer die de GUID herkent, ontleedt het blok; een lezer die dat niet doet, is verplicht het over te slaan. Dat is het hele verhaal van voorwaartse compatibiliteit, en dat is waarom Excel 2007 een sparkline-werkboek probleemloos opent — het toont simpelweg de brongetallen zonder de symbolen

Eén detail van het mechanisme is de moeite waard om te vermelden omdat het in strijd is met een algemene aanname: er is geen mc:Ignorable-declaratie nodig op de root van het werkblad. De GUID-poort van de ext is op zichzelf voldoende, en Excel's eigen schrijver declareert de x14-naamruimte lokaal op het <ext>-element in plaats van de root aan te raken. HotXLS volgt hetzelfde patroon, waardoor de rest van de XML van het werkblad ongewijzigd blijft. Dit is ook het structurele verschil met echte grafieken: een grafiek is een afzonderlijk DrawingML-onderdeel met een eigen renderingpijplijn, behandeld in het bijbehorende artikel over grafieken, afbeeldingen en tekeningen, terwijl a sparkline werkbladmetadata is die het spreadsheetraster zelf tekent

Hoe voegt u programmatisch sparklines toe aan een XLSX-bestand?

TXLSXWorksheet.AddSparklineGroup(AType, ASourceRange, ALocation) is de weg via één aanroep: het maakt een groep van het opgegeven type aan, voorziet deze van Excel's standaardreekssleur en voegt de eerste sparkline toe die een bladgekwalificeerd bronbereik aan een hostcel koppelt. De geretourneerde TXLSXSparklineGroup accepteert vervolgens meer sparklines via haar methode Add, één F/Sqref-paar per rij, die allemaal het type en de opmaak van de groep delen. Voor het dashboardscenario is dat precies de vorm die u wilt: één groep, één set visuele opties, honderden leden

var
  Book: TXLSXWorkbook;
  Sheet: TXLSXWorksheet;
  Grp: TXLSXSparklineGroup;
  Row: Integer;
begin
  Book := TXLSXWorkbook.Create;
  try
    Book.Open('dashboard.xlsx');
    Sheet := Book.Sheets[0];

    // Eén groep: elke productrij deelt type en opmaak
    Grp := Sheet.AddSparklineGroup(xlsxSparklineLine,
      'Sheet1!B2:M2', 'N2');
    for Row := 3 to 201 do
      Grp.Add(Format('Sheet1!B%d:M%d', [Row, Row]),
        Format('N%d', [Row]));

    Book.SaveAs('dashboard-trends.xlsx');
  finally
    Book.Free;
  end;
end;

TXLSXSparklineType dekt de drie soorten die Excel aanbiedt: xlsxSparklineLine voor trendlijnen, xlsxSparklineColumn voor miniatuurstaafdiagrammen en xlsxSparklineStacked voor winst/verlies-stroken waarbij elke waarde als een omhoog- of omlaag-blok wordt gerenderd. Elk lid-sparkline bestaat uit slechts twee strings — F, het bladgekwalificeerde bronbereik, en Sqref, de cel waarin het symbool wordt getekend — dus er valt verder niets per rij te configureren. Groepen kunnen ook volledig handmatig worden opgebouwd via de verzameling Worksheet.SparklineGroups wanneer u met een lege groep wilt beginnen in plaats van met de standaardgroep

Markeringen, kleurslots en asschaling op het getypeerde model

TXLSXSparklineGroup stelt de volledige optieset van Excel's lint voor Sparkline-hulpprogramma's beschikbaar als gewone eigenschappen. Zes booleaanse schakelaars benadrukken gegevenspunten: Markers voor elk punt op een lijn-sparkline, plus HighPoint, LowPoint, NegativePoints, FirstPoint en LastPoint. Acht ARGB-kleurslots zijn eraan gekoppeld — ColorSeries, ColorNegative, ColorAxis, ColorMarkers, ColorFirst, ColorLast, ColorHigh, ColorLow — met de conventie dat een slot dat op 0 staat wordt weggelaten uit het bestand en Excel haar eigen standaard toepast. Dezelfde volledig ondoorzichtige ARGB-vorm verschijnt in de hele bibliotheek, dus $FFD00000 betekent hier dezelfde kleur rood als in stijlen voor voorwaardelijke opmaak

Grp := Sheet.AddSparklineGroup(xlsxSparklineStacked,
  'Sheet1!B2:M2', 'N2');
Grp.NegativePoints := True;          // verliesmaanden vallen op
Grp.ColorNegative := $FFD00000;
Grp.HighPoint := True;
Grp.ColorHigh := $FF00B050;
Grp.MinAxisType := xlsxSparklineAxisGroup;   // één schaal voor alle rijen
Grp.MaxAxisType := xlsxSparklineAxisGroup;
Grp.DisplayEmptyCellsAs := xlsxSparklineEmptyGap;

De aseigenschappen verdienen een moment van aandacht voordat u de standaarden accepteert. MinAxisType en MaxAxisType nemen elk individuele, groeps- of aangepaste schaling aan: individuele schaling rekt elke sparkline uit naar haar eigen minimum en maximum, waardoor een rij die schommelt tussen 99 en 101 er net zo dramatisch uitziet als een rij die verdriedubbelt. Groepsschaling plaatst alle leden op één gedeelde verticale schaal, zodat de hoogte van het symbool in elke rij dezelfde hoeveelheid betekent — meestal wat een vergelijkend dashboard wil. DisplayEmptyCellsAs bepaalt wat een lege broncel doet met een lijn: een gat achterlaten, als nul tellen of het gat overbruggen door de buren te verbinden

Wat HotXLS bij het opslaan in de extLst van het werkblad schrijft

Bij het opslaan serialiseert HotXLS elke groep als een x14:sparklineGroup-element dat de type- en schakelattributen draagt, de kleurslots als kind-elementen, en een x14:sparklines-lijst waarvan de leden een xm:f-bereikformule koppelen aan een xm:sqref-hostcel. De hele structuur bevindt zich in <extLst><ext uri="{05C60535-1F16-4fd2-B633-F4F36F0B64E0}"> aan het einde van het werkbladonderdeel, met de x14-naamruimte direct op het blok gedeclareerd, precies zoals Excel het schrijft. Een werkblad zonder sparklinegroepen genereert helemaal geen extLst, zodat bestanden die de functie nooit hebben gebruikt er byte-voor-byte identiek uitkomen aan wat eerdere HotXLS-versies produceerden — dezelfde discipline van een conservatieve schrijver die is beschreven in het artikel over verliesvrije heen-en-weerinfrastructuur

Eén detail van de specificatie is belangrijk als u ooit de uitvoer vergelijkt met die van Excel of de XML handmatig controleert: de standaardinstelling van het schema voor lineWeight is 0,75 punten, en niet de 1,25 die sommige secundaire bronnen citeren — [MS-XLSX] definieert CT_SparklineGroup@lineWeight met een standaardwaarde van 0,75, wat overeenkomt met de dikte die de UI van Excel daadwerkelijk toepast. HotXLS respecteert de standaard door weglating: stel LineWeight in op iets anders dan 0,75 en het attribuut wordt geschreven, laat het op 0,75 en dat gebeurt niet. Als u dit in een van beide richtingen verkeerd doet, produceert u bestanden die er subtiel dikker of dunner uitzien dan hetzelfde werkboek dat vanuit Excel is opgeslagen

Heen-en-weerrondes, bladkopieën en extensies die HotXLS niet modelleert

Openen is het spiegelbeeld van opslaan. Wanneer HotXLS een werkboek opent dat sparklines bevat — of HotXLS het nu heeft geschreven of Excel — controleert de parser de GUID van de sparkline-ext en bouwt elke groep, elk lid, elke kleur en elke asoptie opnieuw op in het getypeerde model, zodat een cyclus van openen→bewerken→opslaan ze volledig behoudt. Het kopiëren van een werkblad naar een ander werkboek kloont haar sparklinegroepen samen met de rest van het blad, wat het genereren van sjabloongestuurde rapporten eenvoudig maakt: houd een gestileerd sjabloonblad aan, kopieer het per rapport en verwijs de bereiken opnieuw

De extLst kan ook extensieblokken bevatten waar HotXLS geen getypeerd model voor heeft — Excel schrijft x14-blokken voor functies zoals slicers en uitgebreide voorwaardelijke opmaak in dezelfde lijst. Sinds v2.131.0 blijven die vreemde blokken behouden tijdens een heen-en-weerronde in plaats van dat ze worden weggegooid: de parser legt ze vast, stelt ze alleen-lezen beschikbaar via Worksheet.RawWorksheetExts, en schrijft ze terug bij het opslaan, en ze reizen mee met een blad wanneer het naar een ander werkboek wordt gekopieerd. De bewaarde XML wordt gereconstrueerd uit parsergebeurtenissen, dus deze is semantisch gelijkwaardig aan het origineel in plaats van byte-identiek, maar Excel accepteert het en de functies overleven. De praktische consequentie is degene die telt in productie: het openen van het werkboek van een klant en het opnieuw opslaan ervan stript niet langer extensies waarvan u HotXLS niet hebt gevraagd ze te begrijpen

De eerlijke grenslijn: sparklines renderen in Excel 2010 en nieuwer, en in elke andere lezer die de x14-extensie implementeert; Excel 2007 toont de gegevens zonder de symbolen, wat de degradatie is waarvoor het formaat is ontworpen. De drie sparklinetypes, de acht kleurslots, de puntschakelaars en de drie asschalingsmodi beslaan het volledige optievlak dat Excel's eigen dialoogvenster biedt. Sparkline-ondersteuning, samen met de rest van de hier getoonde XLSX-engine, wordt geleverd in het HotXLS Delphi Excel Component voor Delphi en C++Builder