Część wykresu zapisana przez bieżącą kompilację Excela rzadko zatrzymuje się na elementach, które definiuje schemat wykresów ECMA-376. Przestrzeń wykresu zwykle otwiera się blokiem mc:AlternateContent, który oferuje c14:style nowszym czytnikom, a gołe c:style starszym, każda seria niesie c:extLst z c16:uniqueId, a etykiety danych „Value From Cells” mieszkają w jeszcze innym rozszerzeniu. Program, który parsuje tylko to, co modeluje, i regeneruje część w chwili zapisu, nie zapisuje z powrotem żadnego z tych elementów. Zmień jedno słowo w tytule, otwórz plik ponownie w Excelu, a etykiety, które ciągnęły tekst z zakresu komórek, zniknęły
Poprawka polega na przestaniu traktować zapis jako przebudowę. Zachowaj bajty, z którymi wykres przyszedł, zapisz je z powrotem niezmienione, gdy nic modelowanego się nie ruszyło, a gdy coś się ruszyło, scal nowe elementy w oryginalne drzewo, tak żeby nieznane części zostały tam, gdzie postawił je Excel
Odtwórz oryginalny ChartML, gdy nic się nie zmieniło
HotXLS robi dokładnie to, gdy TXLSXWorkbook.PreserveUnsupportedParts jest True, co jest wartością domyślną na świeżo utworzonym skoroszycie. Przy imporcie każdy wykres zachowuje swoje oryginalne bajty UTF-8 razem z długością i 64-bitowym odciskiem palca treści, którą reprezentuje model typowany. W chwili zapisu HotXLS regeneruje modelowaną część, porównuje długość i odcisk palca, i jeśli oba pasują, zapisuje zapisane bajty z powrotem dosłownie: bez przestawiania elementów, bez dryfu białych znaków i bez drugiej kopii UTF-16 każdego nietkniętego wykresu trzymanej w pamięci
Ponieważ porównanie biegnie po treści, przypisanie właściwości wartości, którą już trzyma, niczego nie zmienia. Settery TXLSXChart wychodzą wcześniej przy równej wartości, więc pętla, która „odświeża” każdy tytuł wykresu z wiersza bazy danych, nadal odtwarza każdy wykres, którego tytuł był już poprawny. Formatowanie linii na słupkach błędów i liniach trendu jest śledzone własną flagą zmiany, a jego zmiana zawsze liczy się jako edycja
TXLSXChart.HasPreservedXml mówi ci, po której ścieżce idzie wykres, zanim cokolwiek zedytujesz. Zgłasza True, gdy do wykresu jest przyczepiony oryginalny ChartML, co jest warunkiem wstępnym i odtworzenia, i scalania
var
Workbook: TXLSXWorkbook;
Chart: TXLSXChart;
begin
Workbook := TXLSXWorkbook.Create;
try
// True domyślnie; ustaw jawnie, żeby intencja przeżyła refaktoryzację.
// Musi być na miejscu przed Open, bo Open buduje stan
Workbook.PreserveUnsupportedParts := True;
Workbook.Open('quarterly-review.xlsx');
Chart := Workbook.Sheets[1].Charts[0]; // Sheets[] liczone od 1, Charts[] od 0
if not Chart.HasPreservedXml then
Log('chart 0 has no original ChartML attached'); // Log to twoja własna rutyna
Chart.Title := Chart.Title; // równa wartość: nadal odtwarza
Workbook.Sheets[1].Charts[1].Title :=
'Revenue by region, Q3 2026'; // ten wykres idzie ścieżką scalania
Workbook.SaveAs('quarterly-review-out.xlsx');
finally
Workbook.Free;
end;
end;
Co przeżywa scalanie, gdy edytujesz jeden wykres?
Krótka odpowiedź: wszystko, czego model typowany nie posiada. Tytuł, serie, pamięci podręczne danych, grupy wykresów i osie są własnością modelu, więc świeżo wygenerowane drzewo je rozstrzyga. Niemodelowane atrybuty, deklaracje przestrzeni nazw, style, listy rozszerzeń i węzły formatowania pochodzą z oryginalnego drzewa i są reinserowane obok dopasowanych węzłów, obok których siedziały
Usuń serię, a scalanie nie może jej wskrzesić z zachowanego XML tylko dlatego, że oryginał wciąż zawiera c:ser na tej pozycji. Wszystko, czego model posiada, jest brane z modelu, nawet gdy odpowiedź modelu brzmi „zniknęło”. To samo dotyczy poziomu atrybutów: gdy czściszesz pogrubienie, kursywę, podkreślenie, przekreślenie albo rozmiar czcionki znanego stylu tekstu, scalanie czyta brakujący atrybut jako czyszczenie i nie kopiuje b ani sz z powrotem z oryginalnego defRPr. Nieznane atrybuty na tym samym elemencie zachowują swoje miejsce
Tytuły podążają za tą samą regułą. Przy imporcie HotXLS zachowuje natywne bogate przebiegi c:title, z ich czcionkami, rozmiarami i znacznikami języka per przebieg, i odtwarza je, dopóki tekst jest nietknięty. Przypisz nowe Title, a ta bogata treść zostaje porzucona; tytuł jest przepisywany jako jeden przebieg stylowany z TitleTextStyle. Scalanie starych przebiegów w nowy tekst znaczyłoby zgadywanie, do którego przebiegu należą nowe słowa, a zgadywanie tutaj wytwarza tytuły, których nikt nie wpisał
Kolejność schematu jest zachowywana tam, gdzie Office jest na nią wybredny. extLst zostaje ostatnim dzieckiem swojego rodzica, jak wymaga schemat. Bloki zgodności znaczników dostają własne potraktowanie: czytnik pomija mc:Choice i czyta mc:Fallback, a gdy ten fallback opakowuje grupę wykresów, którą model zna, scalanie zapisuje nową typowaną treść z powrotem do poddrzewa fallbacku. Naiwne scalanie wyemitowałoby edytowaną grupę wykresów i zostawiło starą kopię wewnątrz gałęzi fallbacku, więc zapisany wykres niósłby tę samą grupę dwa razy
Wyłączanie jednego wykresu przez ClearPreservedXml
TXLSXChart.ClearPreservedXml porzuca oryginalny XML i odcisk palca dla jednego wykresu, a następny zapis przebudowuje ten wykres czysto z modelu typowanego. To decyzja per wykres. PreserveUnsupportedParts wciąż rządzi tym, czy stan zachowywania jest budowany przy imporcie, i pokrywa też inne niewspierane części pakietu, nie tylko wykresy, dlatego wyłączanie go, żeby dostać jeden czysty wykres, to zła dźwignia
Czyść wykres, gdy odrzucenie jest celem operacji, na przykład wykres szablonowy, który wciąż niesie formatowanie z wycofanego korporacyjnego motywu. Zostaw to w spokoju jako odruch debugowy. Wykres, który renderuje się źle po edycji, to zwykle pytanie modelowania, a czyszczenie zachowanego XML wyrzuca jedyną kopię tego, co Excel napisał oryginalnie
Jeden przypadek potrzebuje najpierw sprawdzenia. Jeśli słupek błędu albo linia trendu używa formatowania linii, którego model typowany nie potrafi przedstawić, jego właściwość LineAppearanceSupported jest False; etykieta linii trendu z formatowaniem poza modelem zgłasza to samo przez LabelAppearanceSupported. HotXLS nie podmieni żadnego z nich po cichu na prostszą wersję: gdy wykres nie ma już zachowanego XML do upadku, zapis staje z EArgumentException nazywającą nieprzedstawialny wygląd linii albo etykiety. Sprawdzenie obu przed czyszczeniem zajmuje jedną pętlę
// Twoja własna funkcja pomocnicza, nie część HotXLS: True, gdy każda linia słupka błędu,
// linia trendu i etykieta linii trendu dają się zapisać z samego modelu typowanego
function ChartRebuildable(Chart: TXLSXChart): Boolean;
var
I, T: Integer;
S: TXLSXChartSeries;
begin
Result := False;
for I := 0 to Chart.SeriesCount - 1 do // obejmuje każdą grupę wykresów
begin
S := Chart.Series[I];
if (S.ErrBarX <> nil) and not S.ErrBarX.LineAppearanceSupported then Exit;
if (S.ErrBarY <> nil) and not S.ErrBarY.LineAppearanceSupported then Exit;
for T := 0 to S.TrendlineCount - 1 do
begin
if not S.Trendlines[T].LineAppearanceSupported then Exit;
if not S.Trendlines[T].LabelAppearanceSupported then Exit;
end;
end;
Result := True;
end;
// ...
Chart := Workbook.Sheets[1].Charts.FindByName('Old theme chart');
if (Chart <> nil) and ChartRebuildable(Chart) then
Chart.ClearPreservedXml // następny zapis przebudowuje ten wykres z modelu
else
Log('kept original ChartML for this chart');
Wykresy kombinatorne z AddPlotGroup i drugą osią
TXLSXChart.AddPlotGroup dopisuje uporządkowaną grupę wykresów z własną rodziną wykresu i własnym przypisaniem osi głównej albo drugorzędnej. To pokrywa wykres, o który prosi każdy zespół finansowy: przychód jako kolumny na lewej osi, procent marży jako linia na prawej. Każdy wykres wystawia ValueAxis, CategoryAxis, SecondaryValueAxis, SecondaryCategoryAxis i SeriesAxis, z typowanymi ustawieniami pozycji, identyfikatorów osi, przecięć, jednostek daty, interwałów etykiet, formatu liczb i widoczności
Płaskie API z wykresów jednogatunkowych wciąż tam jest i mapuje się na grupy. ChartType i AddSeries adresują pierwszą grupę wykresów; SeriesCount i Series[] przechodzą serie każdej grupy po kolei, dlatego funkcja pomocnicza wyżej widzi też serię linii. Kolejność ma znaczenie w jednym miejscu. TXLSXCharts.Add tworzy wykres bez żadnej grupy wykresów, i ta z AddSeries albo AddPlotGroup, która pobiegnie pierwsza, tworzy grupę jeden. Wołaj AddPlotGroup(xlsxChartLine, xlsxAxisSecondary) pierwszy na świeżym wykresie, a linia staje się grupą jeden, typ wykresu przełącza się na linię, i seria przychodu, którą dodasz następna, ląduje w niej. Utworzenie obu grup jawnie usuwa niejednoznaczność, więc przykład tak robi
var
Chart: TXLSXChart;
Columns, Line: TXLSXChartPlotGroup;
begin
// Zakotwiczony na wierszach 1..15, kolumnach 5..12; żadna grupa wykresów jeszcze nie istnieje
Chart := Sheet.Charts.Add(xlsxChartColumn, 'Revenue and margin',
1, 5, 15, 12);
Columns := Chart.AddPlotGroup(xlsxChartColumn); // grupa 1, główna
Columns.AddSeries('Revenue', 'Data!$A$2:$A$13', 'Data!$B$2:$B$13');
Line := Chart.AddPlotGroup(xlsxChartLine, xlsxAxisSecondary); // grupa 2
Line.AddSeries('Margin %', 'Data!$A$2:$A$13', 'Data!$C$2:$C$13');
Line.ZOrder := 1; // narysuj linię przed kolumnami
Chart.SecondaryValueAxis.NumberFormat := '0%';
Chart.SecondaryValueAxis.NumberFormatSourceLinked := False;
Chart.SecondaryValueAxis.MinValue := 0;
Chart.SecondaryValueAxis.MaxValue := 0.5;
// Obszar kreślenia jako ułamki wykresu: x, y, szerokość, wysokość w jednym wywołaniu
Chart.PlotAreaLayout.SetPositionAndSize(0.08, 0.12, 0.80, 0.72);
end;
ZOrder na grupie wykresów steruje warstwami: zapis wykresów sortuje grupy według niego przed wyemitowaniem bloków c:barChart i c:lineChart, niższe wartości z tyłu. Ponieważ każda uporządkowana grupa jest modelowana, otwarcie i ponowny zapis wykresu kombinarnego trzyma każdą rodzinę wykresu razem z jej osiami drugorzędnymi, daty i serii. Referencje formuł wykresu i buforowane wartości serii przeżywają kopie skoroszytu i cykle otwórz/zapis, a kotwice wykresów są dopasowywane do swoich części wykresu po identyfikatorze relacji, więc pakiet, którego wpisy relacji przestawiło inne narzędzie, nadal kładzie każdy wykres na jego własnej pozycji arkusza
Dlaczego Office ignoruje ręczny układ?
Bo współrzędne przybyły niekompletne. Office wymaga x, y, w i h jako kompletnej grupy, kiedykolwiek jakakolwiek z nich jest obecna w c:manualLayout, i ignoruje cały element w przeciwnym razie. Schemat znaczy wszystkie cztery jako opcjonalne, więc plik z samymi x i y jest poprawny i nadal nie robi niczego w Excelu
TXLSXChartLayout.SetPositionAndSize to wywołanie do użycia. Ustawia wszystkie cztery wartości, znaczy cztery elementy trybu jako obecne i włącza układ w jednym kroku. Pojedyncze właściwości X, Y, Width i Height każda ustawia jedną wartość, a HotXLS zapisuje dokładnie te elementy, które są obecne, tak samo, jak zachowuje obecność każdego z dziewięciu opcjonalnych dzieci przy czytaniu pliku. To trzyma nietknięty układ wierny swojemu źródłu. Znaczy to też, że układ zbudowany z dwóch przypisań właściwości jest zapisywany z dwiema współrzędnymi, a Office go pominie. Ta sama klasa TXLSXChartLayout siedzi za PlotAreaLayout, TitleLayout, LegendLayout i TitleLayout każdej osi
Dwa domyślne idą za tym, co robi Excel, a nie za wszystkim, co pozwala specyfikacja. Nowy układ używa edge dla XMode i YMode oraz factor dla WidthMode i HeightMode; standard pozwala na inne kombinacje, ale Excel zapisuje pozycje jako krawędzie, a rozmiary jako współczynniki i konwertuje cokolwiek innego przy zapisie. A layoutTarget (Target, wewnętrzny albo zewnętrzny) jest zapisywany dla obszaru kreślenia, podczas gdy układy tytułu, tytułu osi i legendy go pomijają, bo notatki implementacyjne Microsoftu dla schematu wykresów nie pozwalają na niego pod tymi elementami
Wpisanie tego w podróż skoroszytu w obie strony
Zachowywanie wykresów to jeden przypadek szerszej reguły: biblioteka, która edytuje dokumenty autorowane gdzie indziej, powinna zmienić to, o co poproszono, i zostawić resztę taką, jaką ją zastała. HotXLS stosuje to samo rozumowanie do motywów, łańcuchów obliczeń i rozszerzeń arkusza, opisane w bezstratnych podróżach w obie strony dla motywu, extLst i calcChain, oraz do projektów VBA i łączy zewnętrznych w zachowywaniu VBA i łączy zewnętrznych. Dla tworzenia wykresów, umieszczania obrazów i rysowania kształtów od zera zobacz wykresy, obrazy i rysunki w HotXLS
HotXLS czyta, edytuje i zapisuje wykresy XLSX z natywnego kodu Delphi i C++Buildera bez zainstalowanego Excela na maszynie, więc wierność wykresów jest rozstrzygana przez bibliotekę, a nie przez serwer automatyzacji. Referencja API wykresów i kompilacja próbna są na stronie komponentu arkusza kalkulacyjnego HotXLS dla Delphi