Technisch artikel

Excel-grafieken bewerken zonder ChartML te verliezen in HotXLS

HotXLS behoudt de originele ChartML van een geïmporteerde Excel-grafiek en speelt hem byte voor byte af wanneer er in het getypeerde model niets is veranderd, zodat het openen en opslaan van een werkmap niet stilzwijgend de delen van een grafiek weghaalt die het objectmodel niet vertegenwoordigt. Bewerk een titel, een reeks of een as en HotXLS voegt structureel de nieuwe getypeerde knopen in in de oorspronkelijke boom in plaats van de grafiek vanaf nul te regenereren

Die onderscheiding — afspelen wanneer onaangeraakt, samenvoegen wanneer bewerkt — is wat een Delphi-applicatie veilig maakt om te richten op grafieken die een analist in Excel heeft gebouwd. Een grafiek regenereren vanuit een getypeerd model verliest altijd wat het model niet kent, en Excel-grafieken zitten vol dingen die een bibliotheek niet kent: custom stijlen, extensielijsten, alternate-content-takken, naamruimte-gekwalificeerde opmaak van add-ins

Waarom maakt een werkmap opslaan zijn grafieken plat?

Omdat de gebruikelijke implementatie leest wat het begrijpt en schrijft wat het begrijpt. Alles ertussenin — een extLst van een nieuwer Excel-build, een mc:AlternateContent-tak, een shape-effect waarvoor het model geen eigenschap heeft — bestaat in het bestand, heeft geen thuis in het objectmodel, en verdwijnt bij het opslaan. De gebruiker ziet een grafiek die zijn gradiënt verloor, zijn custom gegevenslabels of zijn 3D-effect, en de werkmap kan niet uitleggen waarom

Met PreserveUnsupportedParts ingeschakeld op de werkmap, slaat HotXLS bij het importeren de originele UTF-8-grafiekbytes op samen met een lengte en een 64-bit-vingerafdruk van de gemodelleerde content. Als het getypeerd model tegen de tijd van opslaan niet is veranderd, schrijft het de originele bytes terug. Geen XML-herschikking, geen whitespace-drift, geen residente UTF-16-kopie van de grafiek die in geheugen rondhangt voor een grafiek die niemand bewerkte

var
  Workbook: TXLSXWorkbook;
  Chart: TXLSXChart;
begin
  Workbook := TXLSXWorkbook.Create(nil);
  try
    Workbook.PreserveUnsupportedParts := True;   // set before Open
    Workbook.Open('quarterly-review.xlsx');
    Chart := Workbook.Sheets[1].Charts[0];   // Sheets[] is 1-based
    if Chart.HasPreservedXml then
      Log('original ChartML retained; untouched charts replay exactly');
    Chart.Title := 'Revenue by region, Q3 2026';  // now the merge path runs
    Workbook.SaveAs('quarterly-review-out.xlsx');
  finally
    Workbook.Free;
  end;
end;

Wat de samenvoeging behoudt en wat het model bezit

De regel is eigendom. Publieke modelknopen — titel, reeksen, caches, plot-groepen, assen — worden beslist door de nieuw gegenereerde boom. Ongemodelleerde subbomen worden overgenomen uit het origineel. Die asymmetrie is bewust, en het is het deel dat een moment nodig heeft om te internaliseren: het verwijderen van een reeks mag niet de oude reeks uit de bewaarde XML doen herleven, dus alles wat het model bezit wordt uit het model genomen, ook wanneer het model "weg" zegt

Niet-eigen content wordt ingevoegd naast de gematchte knopen waarnaast het zat, en extLst blijft laatste binnen zijn ouder omdat dat is waar het schema het vereist. Het wissen van een bekende eigenschap — een vet-vlag weghalen, een kleur laten vallen — wordt behandeld als een wissen, niet als een gat dat uit de oorspronkelijke boom moet worden gevuld. Onbekende attributen in dezelfde buurt behouden hun plaats

Markup-compatibility-content krijgt specifieke afhandeling. Bij het parsen slaat HotXLS mc:Choice over en leest mc:Fallback; als die fallback-tak een plot-groep wrapt die het model kent, schrijft de samenvoeging de nieuwe getypeerde content terug in de fallback-subboom. Zonder die stap zou een opslaan de nieuwe plot-groep emitteeren en de oude in de fallback laten zitten, en zou de grafiek er twee meedragen

Eén grafiek uitschakelen

ClearPreservedXml laat de originele XML en vingerafdruk van één grafiek vallen, zodat de volgende opslaan die grafiek puur vanuit het getypeerde model herbouwt. Het is een per-grafiek-beslissing; de werkmappeniveau PreserveUnsupportedParts blijft bepalen of de bewaarstatus bij import wordt opgebouwd

Grijp ernaar wanneer je een schone grafiek wilt in plaats van een trouwe — bijvoorbeeld wanneer een sjabloongrafiek opmaak meedraagt van een oud bedrijfsthema en het punt van de operatie is die weg te gooien. Grijp er niet naar als een debug-reflex: een grafiek die na een bewerking verkeerd rendert, is vaker een modelleringvraag dan een bewaarvraag, en het wissen van de bewaarde XML vernietigt het bewijsmateriaal

Combinatiegrafieken en de tweede as

AddPlotGroup bouwt geordende combinatiegrafieken, waarbij elke plot-groep zijn eigen grafiekfamilie behoudt en zijn eigen primaire of secundaire as-toewijzing. Dat is wat de alomtegenwoordige bedrijfsgafiek nodig heeft: omzet als kolommen op de linker-as, margepercentage als lijn op de rechter

Grafieken ontsluiten primaire en secundaire categorie- en waarde-assen plus een reeksas, met getypeerde categorie-, waarde-, datum- en reeksas-instellingen die posities, identifiers, kruisingen, datum-eenheden, label-intervallen en zichtbaarheid dekken. De oudere platte API — ChartType, AddSeries, de platte Series-verzameling — adresseert nog steeds de primaire plot-groep, zodat bestaande code blijft werken terwijl het model eronder elke geordende groep vasthoudt

var
  Chart: TXLSXChart;
  Line: TXLSXChartPlotGroup;
begin
  // Column chart anchored over rows 1..15, columns 5..12
  Chart := Sheet.Charts.Add(xlsxChartColumn, 'Revenue and margin',
    1, 5, 15, 12);
  Chart.AddSeries('Revenue', 'Data!$A$2:$A$13', 'Data!$B$2:$B$13');
  // Second plot group: a line, read against the secondary value axis
  Line := Chart.AddPlotGroup(xlsxChartLine, xlsxAxisSecondary);
  Line.AddSeries('Margin %', 'Data!$A$2:$A$13', 'Data!$C$2:$C$13');
end;

Vóór dit model bestond, klapte het openen en opslaan van een combinatiegrafiek hem ineen tot één grafiekfamilie en gingen de secundaire, datum- en reeksassen verloren. Grafiek-formulereferenties en gecachete reekswaarden overleven nu ook werkmapkopieën en open/save-cycli, en grafiekankers worden via relatie-identifier aan hun grafiekparts gekoppeld — zodat een applicatie die relatie-entries herschikt niet langer grafiekcontent tussen werkbladposities omwisselt

De manual-layout-val

Als je handmatig een plot-gebied, titel, as-titel of legenda positioneert, schrijf alle vier de coördinaten. Office vereist x, y, w en h als een complete groep telkens wanneer er een van aanwezig is, en negeert de hele manualLayout anders. HotXLS emitteert daarom altijd de volledige rechthoek, en SetPositionAndSize stelt en schakelt hem in één aanroep in

Twee verdere details volgen Excel in plaats van de volle vrijheid van de specificatie. Positie wordt geschreven met xMode en yMode op edge terwijl grootte factor gebruikt voor wMode en hMode, omdat dat is wat Excel schrijft en waar het andere combinaties in omzet. En layoutTarget wordt alleen voor het plot-gebied emitteerd, nooit voor titels of legenda's, waar de implementatienotities van Microsoft het verbieden

Dit passen in een werkmap-round-trip

Grafiekbehoud is één instantie van een algemeen beleid: een bibliotheek die elders ontworpen documenten bewerkt, zou moeten veranderen wat er werd gevraagd te veranderen en niets anders. Dezelfde redenering drijft HotXLS aan op thema's, berekeningsketens en werkbladextensies, beschreven in de notities over verliesvrije round-trips voor thema, extLst en calcChain, en over VBA-projecten en externe links in VBA en externe links behouden. Voor het grafiek-ontwerpopvlak zelf — grafieken maken, afbeeldingen en tekeningen plaatsen vanuit Delphi — zie de doorloop van grafieken, afbeeldingen en tekeningen in HotXLS

HotXLS leest, bewerkt en schrijft XLSX-grafieken vanuit native Delphi en C++Builder-code zonder Excel-installatie op de machine, wat is wat fidelity een bibliotheekprobleem maakt in plaats van een automatiseringsprobleem — de HotXLS-spreadsheetcomponentpagina heeft de grafiekfunctielijst en een trial-download