HotXLS skriver sekundära axelgrupper i BIFF8-diagram genom att avge ett andra AxisParent-block, inte genom att lägga till en andra diagramgrupp efter axlarna. I den klassiska XLS-diagramunderströmmen ligger varje diagramgrupp — ChartFormat, diagramtypposten och CrtLink — i sitt eget axelgruppsblock, och varje serie binds till en genom SerToCrt. Få den nästlingen bakvänd och det finns ingen andra diagramgrupp som en serie kan bindas till, hur många axelposter du än avger
Varför binder en andra diagramgrupp efter axlarna ingenting?
Grammatiken är hela svaret, och den ryms på en rad ABNF. Regeln CHARTFOMATS i [MS-XLS] 2.1.7.20.1 säger AxesUsed 1*2AXISPARENT och skriver sedan ut AXISPARENT = AxisParent Begin Pos [AXES] 1*4CRT End. Läs de två produktionerna tillsammans så faller formen ut: diagramgrupperna är barn till en axelgrupp, inte syskon till den. En diagramunderström med två axelpar och en avslutande diagramgrupp är inte ett diagram med två axlar och en layoutdetalj; det är ett diagram med en diagramgrupp och en uppsättning föräldralösa axelposter. Det spelar roll eftersom SerToCrt ($1045), som ligger inne i SERIESFORMAT-blocket, bär ett nollbaserat diagramgruppsindex, inte ett axelindex. Att skriva crt = 1 när bara ett CRT-block finns pekar en serie på en diagramgrupp som aldrig avgetts. Intuitionen som lurar människor är postnamnet: AXESUSED ($1046) låter som om den räknar axlar, så det naturliga nästa steget är att avge fler axlar. Den räknar axelgrupper, och varje axelgrupp drar med sig ett komplett plottområde och en diagramgrupp
Markera en serie för den sekundära axelgruppen
På HotXLS-sidan kokar detta ner till en boolesk variabel. TXLSChartSeriesInfo har ett fält SecondaryAxis, och när du sätter det på någon serie i arrayen du skickar till TXLSWorksheets.AddChartSheet växlar hela byggaren till läge med två grupper. Det finns inget separat anrop för att "aktivera sekundära axeln" och ingen parameter för antal axlar, eftersom antalet kan härledas: om någon serie vill ha den sekundära gruppen behöver diagrammet två
var
Wb: TXLSWorkbook;
Series: array [0..1] of TXLSChartSeriesInfo;
begin
Wb := TXLSWorkbook.Create;
try
Wb.Sheets.Add.Name := 'Data';
// ... fyll A1:C12 med kategorier, intäkter och marginal ...
Series[0] := Default(TXLSChartSeriesInfo); // använd aldrig FillChar för denna post
Series[0].Name := 'Revenue';
Series[0].Categories := 'Data!$A$1:$A$12';
Series[0].Values := 'Data!$B$1:$B$12';
Series[1] := Default(TXLSChartSeriesInfo);
Series[1].Name := 'Margin';
Series[1].Categories := 'Data!$A$1:$A$12';
Series[1].Values := 'Data!$C$1:$C$12';
Series[1].SecondaryAxis := True; // AXESUSED blir 2
Wb.Sheets.AddChartSheet('Dual Axis', xlsChartTypeLine,
'Revenue vs Margin', '', '', Series);
Wb.SaveAs('dual-axis.xls', xlExcel97);
finally
Wb.Free;
end;
end;
Raden Default(TXLSChartSeriesInfo) är inte dekoration. TXLSChartSeriesInfo blandar hanterade fält (namnen av typen WideString, de dynamiska arrayerna för trendlinje och felstaplar) med vanliga Boolean-medlemmar, och Delphi garanterar bara att de hanterade fälten rensas åt dig. Lämna SecondaryAxis oinitierad och den är vad som än låg på stacken, vilket i praktiken betyder att samma binär producerar ett diagram med en axel från ett konsolvärd och ett diagram med två axlar under testköraren. Kategori- och värdeintervallen löses samtidigt genom arbetsbokens EXTERNSHEET-tabell innan byggaren någonsin ser dem — samma indexmaskineri som täcks i hur HotXLS klassificerar externa BIFF SupBook- och XTI-länkar — så ett intervall som namnger ett okänt blad försämras till en tom platshållare BRAI i stället för att bygget misslyckas
Vad avger HotXLS när en serie är sekundär?
Emitteraren ändrar formen, inte bara ett värde. Utan en sekundär serie innehåller AXESUSED 1 (eller 0 för paj och 3D-paj, som inte har några axelgrupper alls) och ett AxisParent-block följer. Med en sekundär serie innehåller AXESUSED 2 och byggaren kör blocket två gånger, med iax — det första ordet i nyttolasten på 18 byte för AxisParent ($1041) — satt till 0 och sedan 1. Varje pass avger Pos, kategori- och värdeaxeln (Axis, $101D), markören PlotArea ($1035), en standardiserad Frame, sedan ChartFormat ($1014), diagramtypposten, CrtLink ($1022) och två End-markörer för att stänga diagramgruppen och axelgruppen. Den sekundära serien binds sedan med SerToCrt crt = 1 och den primära behåller crt = 0. En post dupliceras avsiktligt inte: legenden avges bara i den första gruppen, eftersom Excel ger ett diagram en legend oavsett hur många axelgrupper det bär. Två andra egenskaper är värda att säga tydligt. Att omstrukturera emitteraren så att diagramgruppen hamnar inne i axelgruppsblocket ändrade inte utdata för vanliga diagram — utan sekundära serier är underströmmen byteidentisk med den tidigare versionen, eftersom parametrisering av AddAxisParent med iax = 0 är exakt den gamla kodvägen. Och byggaren avger fortfarande ett helt axelpar per grupp, så en sekundär grupp kommer alltid med en egen kategori axel även när du bara bryr dig om dess värdeskala
Hur återställer diagraminspektionen axelgruppsbindningen?
Läsningen är två pass över postlistan och måste vara det, eftersom AXESUSED kommer före blocken den beskriver. Det första passet letar bara efter $1046 och läser dess första ord som antalet axelgrupper. Det värdet börjar på 1 och höjs, aldrig sänks: HotXLS tar maximumet av det aktuella antalet och det deklarerade, så en felaktig eller dubblerad AXESUSED inte kan försämra ett diagram som redan har konstaterats deklarera två grupper. Det andra passet håller reda på den aktuella axelgruppen, uppdaterar den vid varje AxisParent och stämplar det indexet på varje Axis-post den möter tills nästa AxisParent dyker upp
var
Model: TXLSChartModel;
i: Integer;
begin
// Sheets[1] är databladet, Sheets[2] diagrambladet
Model := Wb.Sheets[2]._Chart.GetChartModel;
try
if Model.AxisGroupCount = 2 then
Writeln('AXESUSED declares a secondary axis group');
for i := 0 to Model.AxisCount - 1 do
Writeln('axis ', i, ' group ', Model.GetAxis(i).AxisGroup);
for i := 0 to Model.SeriesCount - 1 do
Writeln('series ', i, ' chart group ', Model.GetSeries(i).ChartGroup);
finally
Model.Free;
end;
end;
Två begränsningar är värda att namnge. TXLSChartModel.AxisGroupCount rapporterar vad filen deklarerar, inte hur många AxisParent-block som faktiskt hittades; en fil som säger 2 och levererar ett block rapporterar 2, och AxisCount är där du märker det. Och TXLSChartAxis.AxisGroup är en positionell stämpel: den registrerar i vilket block en axel lästes, vilket är det enda formatet berättar för dig. På seriesidan är avkodningen av SerToCrt villkorad av att vara inne i ett Series-block, eftersom samma post-ID förekommer i sammanhang där det inte är en seriebindning och en oreglerad avkodare gärna skulle skriva över fel serie
Verifiera sekundära axlar utan en riktig Excel-fil
Verifieringen här behövde ingen Excel-fil med en sekundär axel, och det är den användbara delen av berättelsen. Strukturell avkodning är en egenskap hos postsekvensen, så en syntetiserad sekvens bevisar den lika exakt som en insamlad. Regressionen bygger AXESUSED med en nyttolast på 2, sedan två AxisParent-block som vart och ett omsluter en kategori- och en värdeaxel, och hävdar att modellen kommer tillbaka med AxisGroupCount = 2, fyra axlar stämplade 0, 0, 1, 1 och de förväntade axeltyperna på det andra paret
// Strukturell verifiering utan någon Excel-fil alls
Chart := TXLSCustomChart.Create(nil, $0600);
try
AddWordRecord($1046, [2]); // AXESUSED: två axelgrupper
AddAxisParentGroup(0); // AxisParent iax=0 + Begin + 2 Axis + End
AddAxisParentGroup(1); // AxisParent iax=1 + Begin + 2 Axis + End
Model := Chart.GetChartModel;
Assert.AreEqual(2, Model.AxisGroupCount);
Assert.AreEqual(4, Model.AxisCount);
Assert.AreEqual(0, Model.GetAxis(1).AxisGroup);
Assert.AreEqual(1, Model.GetAxis(2).AxisGroup);
finally
Model.Free;
Chart.Free;
end;
Två praktiska anteckningar om du själv syntetiserar poster. TXLSCustomChart.AddData(RecID, Len, nil) derefererar nyttolasten när Len inte är noll, så markörerna Begin ($1033) och End ($1034) måste läggas till med längden noll, inte med en nil-blob och en gammal längd. Och en syntetisk sekvens bevisar avkodaren, aldrig Excels acceptans av din utdata — skrivsidan strukturerades från ABNF och kontrollerades sedan genom en tur och retur via GetChartModel, med stämplar per axel 0/0/1/1 och diagramgrupper per serie 0/1, och den byteidentiska vägen utan sekundär grupp är säkerhetsnätet. Det är samma konservativa hållning som resten av HotXLS-stödet för diagram, bilder och ritningar i Delphi bygger på: avkoda vad posterna säger och vägra gissa en binär layout du inte har läst i specifikationen. Den fullständiga Delphisviten körde 1650 av 1650 på Win32 och Win64 efter att skrivsidan landat
Chart3d-scenparametrar och fAuto-fällan
Två närliggande detaljer biter den som går längre än standarddiagrammet. Den första är Chart3d ($103A, [MS-XLS] 2.4.46), en plan nyttolast på 14 byte som avges inne i diagramgruppen för 3D-varianter: anRot (rotation, 0 till 360), anElev (höjd, signerad, -90 till 90), pcDist (perspektivavstånd, 0 till 100, ignoreras om inte fPerspective är satt), pcHeight och pcDepth (procent av diagrammets bredd, 5 till 500), pcGap (0 till 500) samt ett grbit vars bitar är fPerspective $0001, fCluster $0002, fAutoscale $0004, f3DScaling $0010 och f2DWalls $0020. Specifikationen lägger till begränsningar som postlayouten inte upprätthåller åt dig: på ett transponerat stapeldiagram får anRot och anElev inte överstiga 44, och på ett pajdiagram får anElev inte vara negativ
Den andra är biten fAuto, och det är den som skapar felrapporten "mina färger ignorerades". LineFormat ($1007), AreaFormat ($100A) och MarkerFormat ($1009) bär alla fAuto i bit 0 av sitt grbit, och när den biten är satt tillämpar Excel sin automatiska stil och behandlar de uttryckliga RGB-värdena, linjestilen, markörformen och markörstorleken bredvid som dekoration. Varje emitterare som skriver en egen seriestil måste rensa bit 0; standardemitterarna behåller den satt just så att Excel väljer paletten. Om du redigerar en befintlig arbetsbok i stället för att bygga en, skiljer sig bevarandereglerna igen och täcks i redigering av Excel-diagram utan att förlora bevarad ChartML
Sekundära axelgrupper, SerToCrt-bindning och den typade diagrammodell som visas här levereras i HotXLS Delphi-kalkylbladskomponenten för Delphi och C++Builder, som läser och skriver BIFF8-diagram utan Excel installerat; produktsidan innehåller den fullständiga referensen för diagramposter och listan över AddChartSheet-överlagringar