Teknik Makale

HotXLS ile Value From Cells Veri Etiketleri (Delphi)

Bir üretici grafiği yeniden yazdığında, C sütununu göstermesi gereken etiketler çıplak sayılar olarak döner ya da bir çubuk 20.0 yerine S1; 2; 20 okur; oysa Excel dosyayı şikayetsiz açar. Value From Cells etiketleri bir Office 2013 grafik uzantısında oturur; HotXLS bu uzantıyı TXLSXChartSeries.DataLabelsRange üzerinden, şema sırasında nokta başına tam etiket ayarlarıyla yazar

Excel Value From Cells etiketlerini nerede tutar?

ECMA-376 grafik şeması (Bölüm 1, §21.2), bu etiketin metnini bir çalışma sayfası aralığından al anlamına gelen hiçbir element taşımaz. Şemanın CT_DLbls türü, bir serinin içindeki <c:dLbls> bloğu, sayısı belirsiz kadar tek noktalık <c:dLbl> geçersiz kılması taşır; ardından ya <c:delete> ya da paylaşılan etiket grubu gelir ve sonunda bir uzantı listesi durur. Paylaşılan grup sabit bir dizidir: numFmt, spPr, txPr, dLblPos, showLegendKey, showVal, showCatName, showSerName, showPercent, showBubbleSize, separator, showLeaderLines, leaderLines. Excelin Veri Etiketlerini Biçimlendir panosundaki neredeyse her onay kutusu bu elementlerden birine oturur. Value From Cells oturmaz. Excel 2013 bunu, standardın üstüne c15 namespaceindeki (http://schemas.microsoft.com/office/drawing/2012/chart) uzantı listeleriyle ekledi ve Microsoft [MS-ODRAWXML] içinde belgeledi; dolayısıyla ECMA-376 şemasından katı biçimde kurulan bir okuyucu orada opak <c:ext> elementlerinden başka bir şey görmez. Önemli kısımlarına indirgenmiş hâliyle, hücre aralığı etiketli bir seri şöyle görünür:

<c:ser>
  <c:idx val="0"/><c:order val="0"/>
  <c:tx>...</c:tx>
  <c:dLbls>
    <!-- sıfır ya da daha fazla <c:dLbl> nokta geçersiz kılması bloğun başında durur -->
    <c:showLegendKey val="0"/>
    <c:showVal val="1"/>
    <c:showCatName val="0"/>
    <c:showSerName val="0"/>
    <c:showPercent val="0"/>
    <c:showBubbleSize val="0"/>
    <c:extLst>
      <c:ext uri="{CE6537A1-D6FC-4f65-9D91-7224C49458BB}"
             xmlns:c15="http://schemas.microsoft.com/office/drawing/2012/chart">
        <c15:showDataLabelsRange val="1"/>
      </c:ext>
    </c:extLst>
  </c:dLbls>
  <c:cat>...</c:cat>
  <c:val>...</c:val>
  <c:extLst>
    <c:ext uri="{02D57815-91ED-43cb-92C2-25804820EDAC}"
           xmlns:c15="http://schemas.microsoft.com/office/drawing/2012/chart">
      <c15:datalabelsRange>
        <c15:f>Sales!$C$1:$C$3</c15:f>
        <c15:dlblRangeCache>
          <c:ptCount val="3"/>
          <c:pt idx="0"><c:v>Target met</c:v></c:pt>
          ...
        </c15:dlblRangeCache>
      </c15:datalabelsRange>
    </c:ext>
  </c:extLst>
</c:ser>

Özellik iki yere bölünmüş durumda. Aralık seriye aittir: {02D57815-91ED-43cb-92C2-25804820EDAC} URIli seri düzeyi uzantı c15:datalabelsRange taşır; bunun c15:fsi başvuruyu, c15:dlblRangeCachesi ise dosya kaydedildiğinde göründükleri hâliyle etiket metinlerini tutar. Anahtar etiketlere aittir: c15:showDataLabelsRange, <c:dLbls>un sonunda {CE6537A1-D6FC-4f65-9D91-7224C49458BB} URIli bir uzantının içinde oturur ve aynı uzantı, tek bir <c:dLbl>i kapatarak aralık metnini tek nokta için açıp kapayabilir

İki yarım da tek başına düşer. Aralığı olup anahtarı olmayan dosya hiç hücre metni göstermez; anahtarı olup aralığı olmayan dosyanın da gösterecek bir şeyi yoktur. Yalnızca §21.2de tanımlı olanı modelleyen bir program, grafik parçasını yeniden ürettiğinde ikisini de kaybeder; HotXLSin bir düzenlemede modellenmemiş ChartMLi koruması bunun içindir. Önbellek, başvuru kadar önemlidir: aralığı çözümleyemeyen bir tüketici — örneğin aralık başka bir çalışma kitabına işaret ediyordur — yine de çizecek metne ihtiyaç duyar

Delphi için HotXLSte Excel Value From Cells etiketlerini taşıyan iki c15 yuvasının şeması: serideki datalabelsRange başvurusuyla önbelleği ve dLbls içindeki showDataLabelsRange anahtarı
aralık, seri düzeyi bir c15 uzantısında; anahtar ise c:dLbls içinde durur — iki yarımdan biri tek başına hiç hücre metni göstermez

Bir seriye etiket aralığını ayarlamak

HotXLSte aralık, serinin bir özelliğidir. TXLSXChartSeries.DataLabelsRange atamak aynı zamanda ShowDataLabelsRangeu açar ve onu açıkça kurulmuş olarak işaretler; çünkü anahtarı olmayan bir aralık asla gösterilmez ve boş bir dizge atamak onu yeniden kapatır. Hücre metni, sıradan anahtarlarla (ShowValue, ShowCategoryName, ShowSeriesName, ShowPercent, ShowLegendKey, ShowBubbleSize) birleşir ve Separatorle birleştirilir; LabelNumFmt ise değer kısmını biçimler. Bir eğilim çizgisinin denklem etiketi bu bloğun parçası değildir; <c:trendline>in içinde yaşar ve eğilim çizgisi denklem metni için Excelin kendi kurallarını izler. Grafik kaydedilirken ResolveDataLabelsRange, DataLabelsCachei hücrelerin biçimlendirilmiş metninden tazeler ve grafik yalnızca görünür hücreleri çizerken gizli satır ve sütunları atlar; başka bir çalışma kitabına giden aralık, açıldığı önbelleği korur. Çalışma kitabı ile grafik oluşturma, her zamanki HotXLS grafik kalıbını izler: yeni bir çalışma kitabında siz bir sayfa ekleyene dek sayfa yoktur ve Sheets.Items 1den sayar

uses
  lxHandleX;

procedure BuildRevenueChart(const FileName: WideString);
var
  Book: TXLSXWorkbook;
  Sheet: TXLSXWorksheet;
  Chart: TXLSXChart;
  Series: TXLSXChartSeries;
begin
  Book := TXLSXWorkbook.Create;
  try
    Sheet := Book.Sheets.Add('Sales');
    Sheet.Cells[1, 1].Value := 'East';
    Sheet.Cells[1, 2].Value := 18.25;
    Sheet.Cells[1, 3].Value := 'Target met';
    Sheet.Cells[2, 1].Value := 'Central';
    Sheet.Cells[2, 2].Value := 20;
    Sheet.Cells[2, 3].Value := 'Restated';
    Sheet.Cells[3, 1].Value := 'West';
    Sheet.Cells[3, 2].Value := 14.6;
    Sheet.Cells[3, 3].Value := 'Below plan';

    Chart := Sheet.AddChart(xlsxChartBar, 'Revenue by region', 1, 5, 16, 12);
    Series := Chart.AddSeries('Revenue', 'Sales!$A$1:$A$3', 'Sales!$B$1:$B$3');

    // Aynı zamanda ShowDataLabelsRange := True yapar ve kurulu olarak işaretler
    Series.DataLabelsRange := 'Sales!$C$1:$C$3';
    Series.ShowValue := True;           // hücre metni artı değer
    Series.Separator := ' | ';
    Series.LabelNumFmt := '0.0';        // değer kısmı 20.0 okur, 20 değil
    Series.LabelPos := xlsxDlblPosOutEnd;

    Book.SaveAs(FileName);
  finally
    Book.Free;
  end;
end;

Şemanın ST_DLblPos sabit listesi dokuz konum belirteci sayar ve bunların herhangi birini herhangi bir grafikte kabul eder; dolayısıyla doğrulayıcı, bir çizgi serisindeki outEnde itiraz etmez. Hangi konumların anlamlı olduğu grafik türüne bağlıdır: çubuk ve sütun serileri ctr, inBase, inEnd ile outEnd alır, pasta grafikler bestFit ekler, çizgi ve dağılım serileri ise l, r, t ile b kullanır. HotXLS, seçilen TXLSXChartDataLabelPos değerini kendi belirteci olarak yazar ve hükmü Excelin olana bırakır; LabelPos da grafik türünüze düşen alt kümeden gelmelidir. xlsxDlblPosDefault atamak LabelPosSeti temizler, hiç dLblPos yazmaz ve etiketleri Excelin yerleştirmesine bırakır. Yukarıdaki örnek için Beklenen: her çubuğun, ucunun dışında, hücre metniyle ve bir ondalıklı değeriyle etiketlenmesi; iki kısmın | ile birleştirilmesi

Delphi için HotXLSte Excel veri etiketi konum belirteçlerinin grafik ailesine göre karar matrisi: çubuk ve sütun, pasta, çizgi ve dağılım; her ailenin aldığı dLblPos belirteçleriyle
şema herhangi bir dLblPos belirtecini herhangi bir grafikte kabul eder — grafik türüne uyan alt kümeden seçin ve yerleşim hükmünü Excelin olana bırakın

Tek noktalık bir etiket neleri tekrar etmek zorundadır?

Seri etiketinin söylediği her şeyi. Şema, nokta geçersiz kılmasını ucuz gösterir: CT_DLbl bir idxtir; ardından ya delete ya da bir etiket grubu gelir (aynı numFmt ile separator arası dizinin önünde isteğe bağlı bir layout ile tx durur) ve isteğe bağlı bir uzantı listesi eklenir; grubun her üyesi isteğe bağlıdır. Yalnızca idx ile showDataLabelsRange uzantısı taşıyan bir <c:dLbl> bu yüzden şemaya uygundur. Excel böyle bir etiketi 20.0 yerine S1; 2; 20 olarak çizer: atlanan her show* anahtarını açık sayar, seri adıyla kategori görünür ve seri sayı biçimini devralmaz. Bir nokta etiketi, o nokta için seri etiketine yama yapmaz; onu bütün olarak değiştirir; Excelin kendi yazdığı nokta etiketlerinin tam anahtar seti taşıması bu yüzdedir. XMLi küçük tutmak için asgari geçersiz kılmalar veren bir üretici doğrulamayı geçer ve yine de yanlış metin gösterir

S1; 2; 20 çizen asgari bir Excel nokta etiketi geçersiz kırması ile Delphi tarafında HotXLSin 20.0 çizen tam değiştirme etiketinin karşılaştırma şeması
bir nokta etiketi, kendi noktası için seri etiketine yama yapmaz onu değiştirir; her geçersiz kırma tam anahtar setini taşır

HotXLS her nokta etiketini tam bir değiştirme olarak yazar. SetPointShowDataLabelsRange(APointIndex, AVisible), aralık metni için tek noktaya açık ya da kapalı bir değer kaydeder; anahtar, idxe dönüşen 0 tabanlı nokta indeksidir. Kaydetme zamanında böyle her nokta bir <c:dLbl> alır: idx, seri sayı biçimi, konum ve anahtarlar grup sırasıyla ve en son o noktanın değerini taşıyan {CE6537A1-…} uzantısı. SetPointLabelText(APointIndex, AText), <c:tx> üzerinden birebir metin ekler; metni, aralık geçersiz kırması ve korunmuş düzeni olan bir indeks yine de tek bir <c:dLbl> alır. Okuma sırasında seri anahtarları seri düzeyinden alınır; nokta etiketlerinin içindeki kopyalar onları asla ezmez. Aşağıdaki kod gelir serisine iki geçersiz kırma kurar ve ardından gelen XML, nokta 1 için beklenen etikettir

procedure OverridePoints(Series: TXLSXChartSeries);
var
  I: Integer;
  Visible: Boolean;
begin
  // İkinci çubuk (idx 1): yalnızca değeri göster, hücre metni olmadan
  Series.SetPointShowDataLabelsRange(1, False);
  // Üçüncü çubuk (idx 2): bütün etiketi birebir metinle değiştir
  Series.SetPointLabelText(2, 'Q3 pending audit');

  for I := 0 to Series.DataLabelRangeOverrideCount - 1 do
    Writeln('point ', Series.DataLabelRangeOverrideIndexes[I],
      ' range text: ', Series.DataLabelRangeOverrideValues[I]);

  if Series.TryGetPointShowDataLabelsRange(0, Visible) then
    Writeln('point 0 has its own override')
  else
    Writeln('point 0 follows ShowDataLabelsRange = ',
      Series.ShowDataLabelsRange);
end;
<!-- Nokta 1 için beklenen XML -->
<c:dLbl>
  <c:idx val="1"/>
  <c:numFmt formatCode="0.0" sourceLinked="0"/>
  <c:dLblPos val="outEnd"/>
  <c:showLegendKey val="0"/>
  <c:showVal val="1"/>
  <c:showCatName val="0"/>
  <c:showSerName val="0"/>
  <c:showPercent val="0"/>
  <c:showBubbleSize val="0"/>
  <c:separator> | </c:separator>
  <c:extLst>
    <c:ext uri="{CE6537A1-D6FC-4f65-9D91-7224C49458BB}"
           xmlns:c15="http://schemas.microsoft.com/office/drawing/2012/chart">
      <c15:showDataLabelsRange val="0"/>
    </c:ext>
  </c:extLst>
</c:dLbl>

Bir grafiği Excelin açması XMLi hakkında az şey kanıtlar

Excel, çocukları sıra dışı olan grafikleri açar; <c:dLbls> içinde bilinmeyen elementler bile olsa özelliklerini doğru geri okur. val="banana" gibi geçersiz bir öznitelik değeri ise Workbooks.Openu düşürür. Öbür tüketiciler daha az hoşgörülüdür. Open XML SDKnin OpenXmlValidatoru sıra dışı çocukları şema hatası olarak bildirir ve şemaya katı bağlanan araçlar onları reddeder; dolayısıyla Excelde çalışan bir grafik, aşağı akışta bir hattı hâlâ bozabilir. Üretilmiş grafik parçalarını OpenXmlValidator ile doğrulayın; Excelde çift tıklamak yalnızca Excelin işi çevirdiğini söyler. Bunlar, §21.2 şemasından gelen en kolay yanlış yapılacak sıra kurallarıdır:

  • <c:dLbls> içinde nokta geçersiz kılmaları önde durur, paylaşılan grup izler ve extLst bloğu kapatır; grubun içinde dLblPos her show* anahtarından önce gelir ve showLegendKey, showValden önce durur
  • Bir nokta etiketinin içinde layout ile tx, idxi izler ve numFmtten önce gelir; noktanın uzantı listesi onu kapatır
  • Bir çubuk serisi idx, order, tx, spPr, invertIfNegative, pictureOptions, dPt, dLbls, trendline, errBars, cat, val, shape, extLst dizilir; bir çizgi serisi, çubuk serisinin invertIfNegative ile pictureOptions taşıdığı yerde marker, shape taşıdığı yerde ise smooth bulundurur
  • Dağılım, alan ve balon serileri aynı iskeleti korur (etiketler, eğilim çizgileri, hata çubukları ve veri, bu sırayla) ama kendi üyeleriyle: dağılım ve balon verisi için xVal ile yVal; bir balon serisinde veriden sonra bubbleSize ile bubble3D
  • Seri düzeyinde alışılmış sürçmeler, dLblsten önce yazılan veri başvuruları, trendlineden önce gelen errBars ile veriden önce gelen smoothtur; Excel bunların hiçbirinden şikayet etmez

Elle konumlanan etiketler nokta etiketine yazılır

Düzen, sezginin şemadan ayrıldığı yerdir. Bir kullanıcı Excelde bir etiketi sürüklediğinde, elle konum o etiketin <c:dLbl>sinin içinde, idxin hemen ardından <c:layout> olarak saklanır. CT_DLblsin layout çocuğu yoktur; dolayısıyla bütün seti konumlandıracağını umarak doğrudan <c:dLbls>in altına bir tane yazan üretici, doğrulayıcının geçersiz çocuk diye işaretlediği bir dosya üretir. Excel o dosyayı açar ve kimsenin istemediği bir şey yapar: düzeni yalnızca ilk etikete uygular, onu bir idx 0 nokta düzeninin koyacağı yere koyar ve kalan etiketlerin genişliklerini yanlış okur; 37.7lik bir etiket genişliği 52.3 olarak döner. Grafik neredeyse doğru görünür; bu da hatanın gözden kaçmasını kolaylaştırır

Bir uzantıdan kopyalanan bir düzen, bütün çalışma kitabını okunmaz kılabilir. Excel bir c15:layoutu bir etiketin uzantı listesinin içinde de tutabilir ve c15 öneki, parçanın üzerinde değil onu saran <c:ext>in üzerinde bildirilir. Düzenleri yerel ada göre bulan kod o elementi de yakalar ve onu <c:dLbls>in içine yapıştırmak öneki bildirimi olmadan taşır; böylece yeniden kurulan grafik parçası bildirilmemiş bir namespace öneki barındırır ve namespace farkında hiçbir XML ayrıştırıcısı onu yüklemez. Düzenleri namespace ile ve doğrudan çocuk konumuyla eşleştirin; asla yalnızca yerel adla değil

HotXLS bir etiket düzenini yalnızca <c:dLbls>in ya da bir <c:dLbl>in doğrudan <c:layout> çocuğundan alır; asla bir uzantının içinden almaz ve nokta indeksiyle anahtarlayarak birebir saklar. LabelLayoutXml ilk etiketin düzenini tutar; doğrudan <c:dLbls>in altında bulunan başıboş bir tane dâhil, çünkü Excel onu öyle yorumlar. PointLabelLayoutCount, PointLabelLayoutIndexes ile PointLabelLayoutXmls kalanları sayar. Grafik yeniden üretilirken o düzen, idx 0 <c:dLbl>ine gider ve her öteki düzen kendi indeksinin <c:dLbl>ine; her biri o noktanın aralık geçersiz kırmasıyla ve metniyle tek bir etikette birleşir

procedure ReportLabelLayouts(const FileName: WideString);
var
  Book: TXLSXWorkbook;
  Sheet: TXLSXWorksheet;
  Series: TXLSXChartSeries;
  C, S, I: Integer;
begin
  Book := TXLSXWorkbook.Create;
  try
    if Book.Open(FileName) <> 1 then
      Exit;
    Sheet := Book.Sheets[1];               // 1 tabanlı sayfa indeksi
    for C := 0 to Sheet.Charts.Count - 1 do
      for S := 0 to Sheet.Charts[C].SeriesCount - 1 do
      begin
        Series := Sheet.Charts[C].Series[S];
        Writeln('chart ', C, ' series ', S,
          ' label range: ', Series.DataLabelsRange,
          ' cached texts: ', Series.DataLabelsCache.Count);
        if Series.LabelLayoutXml <> '' then
          Writeln('  first label has a manual layout');
        for I := 0 to Series.PointLabelLayoutCount - 1 do
          Writeln('  point ', Series.PointLabelLayoutIndexes[I],
            ' layout: ', Series.PointLabelLayoutXmls[I]);
      end;
  finally
    Book.Free;
  end;
end;

Excel bir nokta etiketini bütün bir etiket olarak okur; dolayısıyla onu öyle yazmak — elle konumu aynı <c:dLbl>in üzerine koyarak — hem S1; 2; 20 metnini hem kaymış ilk etiketi grafiklerinizin dışında tutar. HotXLSte bu model, DataLabelsRangein, nokta geçersiz kılma çağrılarının ve korunan düzen özelliklerinin arkasındadır