HotXLS, Excel waterfall ve treemap grafiklerini bağımsız cx:chartSpace parçaları olarak yazar — xl/charts/chartExN.xml, application/vnd.ms-office.chartex+xml içerik türünü taşır — ve onları bir mc:AlternateContent sarmalayıcıyla çapalayarak Excel 2016 ve sonrasının grafiği render etmesini, eski okuyucuların ise bir fallback şekli almasını sağlar. xlsxChartWaterfall ya da xlsxChartTreemap seç ve bütün parça hattı arkadan gelir
Bu sorun genellikle kendini bir destek biletiyle duyurur. Bir müşteri, finans ekibinin Excel 2019'da kurduğu bir çalışma kitabı gönderir; raporlama aracın onu açar, kaydeder ve waterfall grafiği kaybolmuştur. İstisna yok, uyarı yok, logda hiçbir şey yok — grafik yalnızca çıktıda yoktur. Klasik grafik kod yolunda aramaya gidersen uzun süre ararsın, çünkü grafik en başından beri hiç orada olmadı
Waterfall ve treemap grafikleri neden c:chartSpace içinde değil?
Çünkü Microsoft onları oraya koyamazdı. ECMA-376 Bölüm 1 §21.2'deki DrawingML grafik şeması, grafik elemanlarını — c:barChart, c:lineChart, c:pieChart ve gerisi — kapalı bir seçim olarak numaralandırır; o yüzden Excel 2016'da waterfall, treemap, sunburst ve funnel eklemek, mevcut her okuyucunun dayandığı bir şemayı kırmak anlamına gelirdi. Yeni tipler bunun yerine paralel bir belgeye gitti: http://schemas.microsoft.com/office/drawing/2014/chartex adresindeki 2014 chartex ad alanında bir cx:chartSpace parçası, klasik grafiklerin yanında saklanır ama yapısal olarak onlarla alakasız. Çizim tarafı iki dünyayı mc:AlternateContent ile diker — ECMA-376 Bölüm 3'teki uyumluluk mekanizması: mc:Choice dalı Requires="cx1" bildirir ve a:graphicData/@uri değeri chartex ad alanı olan, yükü çıplak bir <cx:chart r:id="rIdN"/> referansından ibaret bir xdr:graphicFrame tutar; mc:Fallback ise Excel'in oraya kendisinin yazdığı bilgiyi içeren bir dikdörtgen tutar. cx1 anlamayan bir okuyucu, Bölüm 3 sözleşmesi gereği fallback'i almak zorundadır
İlişki türü de diğer ipucu. Klasik bir grafik, standart OOXML grafik ilişkisiyle çizim parçasına asılır; bir chartEx parçası bunun yerine [MS-ODRAWXML]'de belgelenen http://schemas.microsoft.com/office/2014/relationships/chartEx ilişkisini kullanır. HotXLS iki dünyayı da sabitler olarak açığa çıkarır — o ilişki için XlsxRtChartEx, içerik türü için XlsxCtChartEx, çapanın ihtiyaç duyduğu iki ad alanı için XlsxNsMsChartEx ve XlsxNsMsChartEx2015 — ve XlsxChartIsChartEx yüklemi, kaydederken bir grafiği genişletilmiş yola yönlendiren şeydir. Kombinasyon grafikleri ve korunmuş klasik grafik XML'i, korunmuş grafik XML'i ve kombinasyon grafikleri makalesinde anlatılan diğer yoldan gider
Excel'in dosyayı açıp açmamasına karar veren dört şema kuralı
Chartex şeması dört belirli noktada klasik olandan daha katıdır ve yanlış yaptığında dördü de aynı faydasız onarım istemini üretir. HotXLS'teki BuildChartExKnownXml ve BuildChartExRelsXml dördünü de içine gömer, ama XML'i elle incelersen bilmeye değerler
- Sıra. Belgede
cx:chartData,cx:chart'tan önce gelmelidir. Bu, klasik grafiklerden edindiğin sezginin tersidir; orada cache'ler plot alanının içindeki serinin içinde yaşar; bir chartEx parçasında veri önde kendi bloğunda durur ve grafik gövdesi ona yalnızca id ile referans verir - Kardinalite.
cx:plotAreatam olarak bircx:plotAreaRegioniçerebilir. Sıfır değil, iki değil. Burada kombinasyon grafiği hikâyesi yok — bir bölge ve içindeki seriler - Tür yerleşimi. Grafik türü hiçbir eleman adı değildir. Microsoft chartex şemasındaki
ST_SeriesLayoutnumaralandırmasından bir token alan,cx:seriesüzerindekilayoutIdözniteliğidir.XlsxChartExLayoutId, HotXLS enum'unu o token'a eşler ve'waterfall'ya da'treemap'döndürür;XlsxChartExLayoutIdToTypeokumada eşlemeyi ters yönde çalıştırır - Klasik grafiklerin hiç ihtiyaç duymadığı ilişki.
cx:chartDatabir<cx:externalData r:id="rId1"/>elemanı tutar ve o ilişkinin, verinin sahibi çalışma sayfasını işaret edenxl/charts/_rels/chartExN.xml.relsiçinde var olması gerekir, çünkü Excelcx:faralık formüllerini çözmek için onu kullanır. Rels parçasını atla ve grafik, nedenini açıklayan hiçbir şey olmadan boş açılır
Delphi'de bir waterfall ya da treemap grafiği nasıl eklersin?
HotXLS'te başka bir grafik eklediğin yolla aynı — genişletilmiş tipler TXLSXChartType üyesidir, o yüzden TXLSXWorksheet.AddChart onları doğrudan alır ve aşağı akıştaki her şey enum değerine göre anahtarlanır. Öğrenilecek ayrı bir chartEx API'si yok
var
Book: TXLSXWorkbook;
Sheet: TXLSXWorksheet;
Chart: TXLSXChart;
begin
Book := TXLSXWorkbook.Create;
try
Book.Open('cashflow.xlsx');
Sheet := Book.Sheets[0];
// Satır/sütun koordinatlarında çapa dikdörtgeni, klasik bir grafikle aynı
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;
Kaydederken HotXLS, XlsxChartIsChartEx değerinin True döndüğünü görür, ayrı bir chartEx sayacından bir parça adı ayırır ve xl/charts/chartEx1.xml, rels dosyası, içerik türü geçersiz kılması ve XlsxChartExAnchorPayloadXml tarafından üretilen mc:AlternateContent çapasını yazar. Bağımsız sayaç önemlidir: iki klasik grafikli ve bir waterfall'lı bir çalışma kitabı, chartEx3.xml değil, chart1.xml, chart2.xml ve chartEx1.xml alır. Treemap'ler sonra waterfall'lardan iki ayrıntıda ayrılır ve ikisi de aynı kurucu tarafından halledilir. Sayısal boyut tür değiştirir — waterfall için cx:numDim type="val", treemap için type="size" olur, çünkü bir treemap dikdörtgeni büyüklüğü çizilen bir değer olarak değil alan olarak kodlar — ve treemap'ler genelde bir kategori hiyerarşisi ister; model bunu TXLSXChartSeries.CategoriesMultiLevel ile ifade eder. Bu bayrağı kur ve S.ResolveCategoryLevels yazma zamanında aralığı çalışma sayfasından okur, S.CategoryLevelCount ve S.CategoryLevels[] arkasında her düzey için bir TXLSXChartDataCache'e böler ve BuildChartExLevelXml, tek bir cx:strDim içinde her düzey için bir cx:lvl yayar — yaprak düzey önce — ECMA-376 Bölüm 1 §21.2'nin c:multiLvlStrCache için kullandığı fiziksel sıralamanın aynısı; o yüzden işaretlemelerin hiçbiri taşınmasa da bu kural taşınır
Chart := Sheet.AddChart(xlsxChartTreemap, 'Revenue by Region',
2, 8, 24, 16);
S := Chart.AddSeries('Revenue',
'Sheet1!$A$2:$B$40', // iki sütun: yaprak etiketi, sonra üst grup
'Sheet1!$C$2:$C$40');
S.CategoriesMultiLevel := True;
Klasik grafiklerdeki her varsayımı kıran cache şekli
Klasik bir grafik cache okuyucusu yazdıysan, üç alışkanlık bir chartEx parçasında sana ihanet eder ve üçü de tek bir tasarım kararından gelir: chartex, DrawingML'in alt elemanlarda sakladığını özniteliklerde ve eleman içeriğinde saklar
- Cache'lenmiş bir nokta
<cx:pt idx="0">42</cx:pt>biçimindedir. Değer eleman içeriğidir. İnecek birc:valt elemanı yoktur; o yüzdenvdüğümleri arayarak yürüyen bir parser hiçbir şey bulamaz ve cache'i boş raporlar ptCountveformatCode,cx:lvlüzerindeki özniteliklerdir. Klasik birc:numCacheiçinde ikisi de alt elemandır- Birden çok hiyerarşi düzeyi, tek bir
cx:strDimiçinde birden çokcx:lvlelemanıdır.c:multiLvlStrCache'inc:strCache'ten ayrı olduğu gibi ayrı bir çok düzeyli cache elemanı yoktur
BuildChartExLevelXml bu şekli yazar ve ParseChartExXml onu geri okur; her değeri dışarı çıkarken XlsxEscapeText üzerinden geçirir, o yüzden ampersand ya da açılı ayraç içeren bir kategori etiketi round trip'tan sağ çıkar. Kurucunun sayısal bir düzey biçim kodu taşımadığında ne yaptığını not etmeye değer: özniteliği düşürmek yerine formatCode="General" yazar, çünkü burada öznitelik, klasik karşılığındaki gibi isteğe bağlı değildir
Tür geri okumasını bozan ad alanı öneki
HotXLS chartEx parser'ının ilk sürümü kusursuz dosyalar yazıyor ve her birini sütun grafiği olarak geri okuyordu. Kök neden tek bir satırdı ve ders, yazacağın her OOXML parser'ına genelleniyor: TXMLReader.Name, adı belgede yazıldığı gibi döndürür, önek dahil. Klasik bir grafik parçasında çoğu eleman öneksiz gelir, o yüzden SameText(Reader.Name, 'series') çalışır ve artık onu düşünmezsin. Bir chartEx parçasında her eleman önekli gelir — okuyucu sana 'cx:series' uzatır, karşılaştırma hepsinde sessizce başarısız olur ve ne istisna ne bozuk XML alırsın; yalnızca boş bir seri listesi ve varsayılana düşen bir grafik türü. Düzeltme, yerel ada göre karşılaştırmaktır; her çağrı noktasında değil, tek bir yerde
// Reader.Name, ad alanı önekini taşır: 'series' değil 'cx: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 her eleman testi için bu yardımcıyı ve öznitelikler için eşdeğer bir AttrVal yardımcısını kullanır, çünkü cx:autoUpdate ve dostları da öneklidir. Seri elemanları gerçekten görülmeye başlandığında tür, karşılaşılan ilk layoutId'den geri gelir ve her cx:series, cx:dataId/@val değeri cx:data/@id ile eşleştirilerek kendi cx:data bloğuna bağlanır. Açılıp da değiştirilmeyen grafikler bunun yerine eski yolu alır: orijinal baytlar FRawChartXml'den yeniden oynatılır, o yüzden dokunulmamış bir müşteri dosyası girdiği gibi geri çıkar — sparkline uzantısı makalesinde sayfa düzeyi uzantıları yöneten muhafazakâr yazar disiplininin aynısı
HotXLS'in bu grafiklerle bilinçli olarak yapmadığı şey
İki sınır açıkça söylenmeye değer, çünkü ikisi de doldurulmayı bekleyen boşluklar değil, seçimler. Yerleşik PDF ve SVG render edicileri waterfall ya da treemap serilerini çizmez: tipler hiçbir çizim dalıyla eşleşmez, o yüzden seri render'ı atlanır ve sayfanın geri kalanı normal biçimde dışa aktarılır — dışa aktarılan bir PDF'de makul görünen bir treemap sahtesi yapmak hiç çizmemekten daha kötü olur, çünkü aynı dosya için Excel'in gösterdiğiyle sessizce çelişir. Ve xlsxChartWaterfall ile xlsxChartTreemap, HotXLS'in modelden kurduğu yalnızca iki genişletilmiş düzendir; ST_SeriesLayout ayrıca sunburst, funnel, box-and-whisker ve histogram tanımlar ve bunlardan herhangi birini içeren bir çalışma kitabı yine de ham XML yeniden oynatma yolundan round trip olur, yalnızca bugün Delphi kodundan birini yazamazsın. Klasik grafik tiplerinin render'ı değişmedi ve grafikler, görseller ve çizimler makalesinde ele alınıyor; chartEx hattı da onun yanında, Delphi ve C++Builder için HotXLS Delphi Excel Component içinde geliyor