HotXLS skriver Excel-waterfall- och treemap-diagram som fristående cx:chartSpace-delar — xl/charts/chartExN.xml med innehållstypen application/vnd.ms-office.chartex+xml — och förankrar dem genom ett mc:AlternateContent-hölje så att Excel 2016 och senare renderar diagrammet medan äldre läsare får en fallback-form. Välj xlsxChartWaterfall eller xlsxChartTreemap och hela delpipelinen följer med
Sättet det här problemet brukar annonsera sig är en supportbiljett. En kund skickar en arbetsbok som deras ekonomiteam byggde i Excel 2019, ditt rapportverktyg öppnar den, sparar den, och waterfall-diagrammet är borta. Inget undantag, ingen varning, ingenting i loggen — diagrammet finns helt enkelt inte i utdata. Letar du efter det i den klassiska diagramkodvägen kommer du att leta länge, för diagrammet fanns aldrig där från början
Varför finns inte waterfall- och treemap-diagram i c:chartSpace?
För att Microsoft inte kunde lägga dem där. DrawingML-diagramschemat i ECMA-376 Part 1 §21.2 räknar upp sina diagramelement — c:barChart, c:lineChart, c:pieChart och resten — som ett slutet val, så att lägga till waterfall, treemap, sunburst och funnel i Excel 2016 hade betytt att bryta ett schema varje befintlig läsare förlitar sig på. De nya typerna hamnade i ett parallellt dokument i stället: en cx:chartSpace-del i 2014 års chartex-namespace, http://schemas.microsoft.com/office/drawing/2014/chartex, lagrad bredvid de klassiska diagrammen men strukturellt orelaterad till dem. Ritningssidan syr ihop de två världarna med mc:AlternateContent, kompatibilitetsmekanismen från ECMA-376 Part 3: mc:Choice-grenen deklarerar Requires="cx1" och håller en xdr:graphicFrame vars a:graphicData/@uri är chartex-namespacen och vars innehåll är en bar <cx:chart r:id="rIdN"/>-referens, medan mc:Fallback håller en rektangel med den notis Excel själv skriver dit. En läsare som inte förstår cx1 är enligt Part 3-kontraktet tvungen att ta fallbacken
Relationstypen är den andra ledtråden. Ett klassiskt diagram hänger i ritningsdelen med den vanliga OOXML-diagramrelationen; en chartEx-del använder http://schemas.microsoft.com/office/2014/relationships/chartEx i stället, dokumenterad i [MS-ODRAWXML]. HotXLS exponerar båda världarna som konstanter — XlsxRtChartEx för den relationen, XlsxCtChartEx för innehållstypen, XlsxNsMsChartEx och XlsxNsMsChartEx2015 för de två namespaces ankaret behöver — och predikatet XlsxChartIsChartEx är det som dirigerar ett diagram ned på den utökade vägen vid sparning. Kombinationsdiagram och bevarad klassisk diagram-XML går den andra vägen, beskriven i artikeln om bevarad diagram-XML och kombinationsdiagram
Fyra schemaregler som avgör om Excel öppnar filen
Chartex-schemat är striktare än det klassiska på fyra specifika ställen, och vart och ett av dem ger samma oanvändbara reparationsprompt när du gör fel. BuildChartExKnownXml och BuildChartExRelsXml i HotXLS bakar in alla fyra, men de är värda att känna till om du någonsin inspekterar XML för hand
- Ordning.
cx:chartDatamåste komma förecx:charti dokumentet. Det är motsatsen till intuitionen du bygger från klassiska diagram, där cacherna bor inuti serien inuti plot area; i en chartEx-del sitter datan längst fram i ett eget block och diagramkroppen refererar bara den via id - Kardinalitet.
cx:plotAreafår innehålla exakt encx:plotAreaRegion. Inte noll, inte två. Här finns ingen kombinationsdiagram-historia — en region, och serien inuti den - Typens placering. Diagramtypen är inte alls ett elementnamn. Det är
layoutId-attributet påcx:series, som tar en token frånST_SeriesLayout-enumerationen i Microsofts chartex-schema.XlsxChartExLayoutIdmappar HotXLS-enumen till den token och returnerar'waterfall'eller'treemap';XlsxChartExLayoutIdToTypekör mappningen baklänges vid läsning - Relationen klassiska diagram aldrig behöver.
cx:chartDatahåller ett<cx:externalData r:id="rId1"/>-element, och den relationen måste finnas ixl/charts/_rels/chartExN.xml.relsoch peka tillbaka på det kalkylblad som äger datan, för Excel använder den för att lösa uppcx:f-intervallformlerna. Uteslut rels-delen och diagrammet öppnas tomt utan något som förklarar varför
Hur lägger du till ett waterfall- eller treemap-diagram i Delphi?
På samma sätt som du lägger till vilket annat diagram som helst i HotXLS — de utökade typerna är medlemmar av TXLSXChartType, så TXLSXWorksheet.AddChart tar emot dem direkt och allt nedströms nycklas på enumvärdet. Det finns inget separat chartEx-API att lära sig
var
Book: TXLSXWorkbook;
Sheet: TXLSXWorksheet;
Chart: TXLSXChart;
begin
Book := TXLSXWorkbook.Create;
try
Book.Open('cashflow.xlsx');
Sheet := Book.Sheets[0];
// Ankarrektangel i rad/kolumnkoordinater, samma som ett klassiskt diagram
Chart := Sheet.AddChart(xlsxChartWaterfall, 'Q3 Cash Flow',
2, 5, 20, 12);
Chart.AddSeries('Movement', 'Sheet1!$A$2:$A$9', 'Sheet1!$B$2:$B$9');
Book.SaveAs('cashflow-waterfall.xlsx');
finally
Book.Free;
end;
end;
Vid sparning ser HotXLS XlsxChartIsChartEx returnera True, allokerar ett delnamn från en separat chartEx-räknare, och skriver xl/charts/chartEx1.xml, dess rels-fil, innehållstypsöverskriften och det mc:AlternateContent-ankare som XlsxChartExAnchorPayloadXml producerar. Den oberoende räknaren spelar roll: en arbetsbok med två klassiska diagram och ett waterfall får chart1.xml, chart2.xml och chartEx1.xml, inte chartEx3.xml. Treemaps skiljer sig sedan från waterfalls i två detaljer, båda hanterade av samma byggare. Den numeriska dimensionen byter typ — cx:numDim type="val" för ett waterfall blir type="size" för en treemap, för en treemap-rektangel kodar storlek som area snarare än som ett plottat värde — och treemaps vill oftast ha en kategorihierarki, som modellen uttrycker genom TXLSXChartSeries.CategoriesMultiLevel. Sätt den flaggan så läser S.ResolveCategoryLevels intervallet ur kalkylbladet vid skrivning, delar upp det i en TXLSXChartDataCache per nivå bakom S.CategoryLevelCount och S.CategoryLevels[], och BuildChartExLevelXml avger ett cx:lvl per nivå inuti en enda cx:strDim, lövnivå först — samma fysiska ordning ECMA-376 Part 1 §21.2 använder för c:multiLvlStrCache, så den konventionen följer med även om ingen av markeringen gör det
Chart := Sheet.AddChart(xlsxChartTreemap, 'Revenue by Region',
2, 8, 24, 16);
S := Chart.AddSeries('Revenue',
'Sheet1!$A$2:$B$40', // två kolumner: lövetikett, sedan föräldragrupp
'Sheet1!$C$2:$C$40');
S.CategoriesMultiLevel := True;
Cacheformen som krossar alla antaganden från klassiska diagram
Har du skrivit en klassisk diagramcache-läsare kommer tre vanor att förråda dig på en chartEx-del, och alla tre kommer från ett designbeslut: chartex lagrar i attribut och elementinnehåll det DrawingML lagrar i barnelement
- En cachad punkt är
<cx:pt idx="0">42</cx:pt>. Värdet är elementinnehållet. Det finns ingetc:v-barnelement att gå ned i, så en parser som letar efterv-noder hittar ingenting och rapporterar en tom cache ptCountochformatCodeär attribut påcx:lvl. I en klassiskc:numCacheär båda barnelement- Flera hierarkinivåer är flera
cx:lvl-element inuti encx:strDim. Det finns inget separat multi-level-cacheelement på det sättc:multiLvlStrCacheär separat frånc:strCache
BuildChartExLevelXml skriver den formen och ParseChartExXml läser tillbaka den, och kör varje värde genom XlsxEscapeText på vägen ut så att en kategorietikett som innehåller ett &-tecken eller en vinkelparentes överlever rundresan. Värt att notera är vad byggaren gör när en numerisk nivå saknar formatkod: den skriver formatCode="General" i stället för att släppa attributet, för här är attributet inte valfritt på det sätt dess klassiska motsvarighet är
Namespace-prefixet som fick typläsningen att misslyckas
Första versionen av HotXLS chartEx-parsern skrev fullt giltiga filer och läste tillbaka var och en av dem som ett kolumndiagram. Grundorsaken var en rad, och läxan generaliserar till vilken OOXML-parser du än kommer att skriva: TXMLReader.Name returnerar namnet som skrivet i dokumentet, prefix inkluderat. I en klassisk diagramdel anländer de flesta element utan prefix, så SameText(Reader.Name, 'series') fungerar och du slutar tänka på det. I en chartEx-del är vartenda element prefixat — läsaren ger dig 'cx:series', jämförelsen misslyckas tyst på dem alla, och du får varken undantag eller felformad XML, bara en tom serielista och en diagramtyp som faller tillbaka på standard. Fixen är att jämföra på local name, på ett ställe i stället för vid varje anropsplats
// Reader.Name bär sitt namespace-prefix: 'cx:series', inte 'series'
function IsEl(const LocalName: string): Boolean;
var
NodeName: string;
begin
NodeName := String(Reader.Name);
Result := SameText(NodeName, LocalName) or
EndsText(':' + LocalName, NodeName);
end;
ParseChartExXml använder den hjälparen för varje elementtest och en motsvarande AttrVal-hjälpare för attribut, eftersom cx:autoUpdate och vänner är prefixade också. När serieelementen väl faktiskt syns kommer typen tillbaka från det första layoutId som stöts på, och varje cx:series kopplas till sitt cx:data-block genom att matcha cx:dataId/@val mot cx:data/@id. Diagram som öppnats men aldrig ändrats tar i stället den äldre vägen: originalbyten spelas upp från FRawChartXml, så en orörd kundfil kommer ut som den gick in — samma konservativa-skrivare-diciplin som styr arkivextensioner i artikeln om sparkline-extensionen
Vad HotXLS medvetet inte gör med dessa diagram
Två gränser är värda att säga rakt ut, för båda är val snarare än luckor som väntar på att fyllas. De inbyggda PDF- och SVG-renderarna ritar inte waterfall- eller treemap-serier: typerna matchar ingen ritningsgren, så serierenderingen hoppas över och resten av arket exporteras normalt — att fejka en trovärdig treemap i en exporterad PDF vore värre än att inte rita något, för den skulle tyst avvika från vad Excel visar för samma fil. Och xlsxChartWaterfall och xlsxChartTreemap är de enda två utökade layouterna HotXLS bygger från modellen; ST_SeriesLayout definierar också sunburst, funnel, box-and-whisker och histogram, och en arbetsbok som innehåller någon av dem rundreser fortfarande genom raw-XML-uppspelningsvägen, du kan bara inte skapa en från Delphi-kod idag. Renderingen av de klassiska diagramtyperna är oförändrad och täcks i artikeln om diagram, bilder och ritningar, och chartEx-pipelinen levereras tillsammans med den i HotXLS Delphi Excel Component för Delphi och C++Builder