Техническа статия

Дублиране на XLSX работен лист в Delphi с HotXLS

Изградили сте един работен лист точно както трябва. Заглавната лента е обединена, ширините на колоните отговарят на данните, горните два реда са закрепени, областта за печат и полетата са настроени за чист A4 експорт, а езичето е оцветено, за да го намери финансовият отдел. Сега отчетът има нужда от дванадесет такива листа, по един за всеки регион, като всички тръгват от същото оформление. Възстановяването на този лист в код дванадесет пъти е точно начинът, по който се промъква незабележим дрейф: регион 7 получава колона с точка по-тясна, регион 11 губи закрепването и никой не забелязва, докато PDF-ът не кацне на бюрото на мениджъра. Това, което всъщност искате, е програмният еквивалент на Excelската команда с десен бутон Move or Copy с отметка Create a copy: вземете завършения лист и щамповайте независими дубликати

XLSX двигателът в HotXLS, нативна библиотека за Delphi и C++Builder, която чете и записва Excel файлове, без да автоматизира самия Excel, вече можеше да премества листове, да изтрива листове и да копира диапазони от клетки между листове. Това, което не можеше да прави до v2.91.0, беше да клонира цял работен лист с едно извикване. Това издание добавя две входни точки: TXLSXWorksheet.CopyFrom, която копира състоянието на ниво лист от един работен лист върху друг, и TXLSXSheets.Duplicate, която добавя нов лист и пуска CopyFrom вместо вас. Интересното не е, че копира неща. Интересното е умишлената граница между това кое се копира дълбоко и кое не, и защо тази граница минава точно там, където минава

Диаграма на входните точки Duplicate и CopyFrom в HotXLS, клониращи Delphi XLSX лист през един споделен копиращ двигател с проверки за nil и самокопиране, връщащ независим лист
Duplicate обвива CopyFrom с нов лист, докато лош индекс връща nil вместо да вдига изключение

Едно извикване за клониране на завършен лист

Операцията от високо ниво е Duplicate. Подайте й единично-базирания индекс на изходния лист и тя връща съвсем нов работен лист, който огледално повтаря оформлението и данните на оригинала. Конвенцията за индексите съответства на Items[] от страната на XLSX, така че първият лист е с индекс 1, а не 0; при подаване на индекс извън обхвата получавате nil вместо изключение, същият договор за неуспех, който използва и останалата част от колекцията листове на XLSX

var
  Book: TXLSXWorkbook;
  Template, Copy: TXLSXWorksheet;
begin
  Book := TXLSXWorkbook.Create;
  try
    Template := Book.Sheets.Add('Template');
    Template.Cells[1, 1].Value := 'Quarterly Statement';
    Template.Range['A1:C1'].Merge;
    Template.ColWidth[1] := 18;
    Template.FreezePanes(2, 1);          // закрепване на горния ред + първата колона
    Template.TabColorIsAuto := False;
    Template.TabColor := $FF1F4E79;

    // Клонирайте с изрично име...
    Copy := Book.Sheets.Duplicate(1, 'Region-North');
    // ...или оставете да се избере подразбираното име в стил Excel.
    Copy := Book.Sheets.Duplicate(1);    // -> "Template (2)"

    Book.SaveAs('regions.xlsx');
  finally
    Book.Free;
  end;
end;

Две неща в този фрагмент заслужават да се разгледат по-бавно. Първо, FreezePanes приема аргументите си с реда напред, FreezePanes(ARow, ACol), така че се подравнява с индексирането Cells[Row, Col]; дубликатът наследява точното разделяне на закрепването. Второ, методът се казва Duplicate, а не по-очевидното Copy, и това не е стилово предпочитание. Copy е стандартна подпрограма в модула System, използвана постоянно за низове и динамични масиви. Метод с име Copy върху клас би го засенчил в телата на методите и би създал точно онзи вид двусмислие при разрешаването на имена, което ви хваща шест месеца по-късно. Duplicate заобикаля целия проблем и се чете правилно на мястото на извикването

Името по подразбиране следва собственото правило на Excel

Когато извикате варианта с един аргумент или подадете празен низ за име, новият лист получава името на източника със суфикс (2), а суфиксът нараства, докато името стане уникално. Дублирайте веднъж листа Template и получавате Template (2); дублирайте го отново и получавате Template (3), защото Template (2) вече е заето. Това огледално повтаря имената, които Excel генерира от собствената си команда Create a copy, така че работната книга, произвеждана от кода ви, изглежда точно така, както потребителят би очаквал да изглежда ръчно дублирана. Проверката за уникалност се изпълнява срещу живата колекция от листове, което означава, че прескача и имена, които сте създали ръчно, а не само тези от предишни дублирания

Ако генерирате по един лист на регион или на месец, облегнете се по-скоро на варианта с изрично име. Предвидимата схема Region-North, Region-South е по-лесна за адресиране по-късно от поредица суфикси (2), (3), и поддържа дефинираните имена и междулистовите формули четими

Какво се копира дълбоко чрез CopyFrom

Под капака Duplicate добавя листа и след това извиква CopyFrom(ASource), който можете да извикате и директно, когато искате да клонирате върху вече създаден лист. CopyFrom се пази от двата изродени случая още в началото: копиране от nil или копиране на лист върху самия него, и двете връщат веднага и не правят нищо. Всичко след това е самото копиране, и то е нарочно широкообхватно

Клетъчните данни идват първи. CopyFrom иска от източника неговия UsedRange, плътната обхващаща рамка на запълнените клетки и обединените области, и използва повторно съществуващата механика CopyRangeTo, за да пренесе всяка стойност, формула и индекс на стила на отделната клетка в целта, започвайки от A1. Върху клетките той възпроизвежда целия пласт състояние на ниво лист, който прави един шаблон да изглежда завършен:

  • Обединените диапазони, пресъздадени по координати, така че банерът да обхване същия правоъгълник
  • Ширините на колоните и височините на редовете, плюс списъците за скритите, свитите и нивата на групиране, копирани дословно, така че нестандартните редове и колони да се подравнят точно
  • Закрепените панели и състоянието на изгледа: ниво на мащабиране, показване на мрежовите линии и нулевите стойности, посока отдясно наляво и типът на изгледа
  • Състоянието на защита с неговите опции за права по действие, така че заключен шаблон да остане заключен по същия начин
  • Целият блок за настройка на страницата: полета, ориентация, размер на хартията, мащабиране и побиране в страница, област за печат, заглавия за печат, горен и долен колонтитул, както и флаговете за печат на мрежовите линии и заглавните редове
  • Диапазонът на автофилтъра, цветът на езичето и видимостта на листа

Резултатът е лист, който се печата, филтрира и представя идентично на своя източник. И тъй като клетките, обединенията и списъците с размери са физически пресъздадени върху новия лист, а не споделени чрез препратка, дубликатът е напълно независим. Запишете 999 в клетка на копието и източникът запазва първоначалната си стойност; тази независимост е най-важното свойство на клон, предназначен за паралелни регионални отчети, и доставяната с библиотеката демонстрация SheetCopy го проверява изрично

Какво оставя плитко и защо

А сега честната част. Диаграмите, вградените изображения, XLSX таблиците, валидациите на данни и правилата за условно форматиране не се копират. Това е документирана и умишлена граница, а не пропуск, и си струва да разберете съображенията зад нея, за да планирате около нея, вместо да се изненадате от нея

Всяка от тези колекции носи идентичност и препратки, които не оцеляват при наивно копиране на полета. Диаграма сочи към диапазон с изходни данни и притежава релация за чертане в OOXML пакета; клонирането на обекта без пренасочване на релацията и препратките към редовете данни произвежда диаграма, която се изобразява срещу грешните данни, или пакет, който Excel маркира като нуждаещ се от ремонт. Таблицата има име, което трябва да е уникално в работната книга, заглавен ред, вързан към конкретни колони, и собствена автоматично генерирана релация. Условните формати и валидациите на данни се прикрепват към координатни диапазони и, при валидациите, могат да реферират други диапазони чрез формула. Дълбокото копиране на което и да е от тях означава пренаписване на препратките и създаване на нови идентичности, което е истинска работа с реални режими на неуспех. Да го направите наполовина, като копирате обекта, но не и препратките му, е по-лошо от това да не копирате изобщо: получава се файл, който се отваря с подкана за ремонт и тихо изпуска съдържание. Затова двигателът копира нещата, които може да копира чисто, и оставя носещите препратки колекции на извикващия, който знае към какво трябва да сочи целта

На практика това означава, че работният процес за по-богат шаблон е: дублирайте листа, за да получите клетките, оформлението и настройката за печат, и след това възстановете диаграмата, таблицата, валидациите или условните формати върху копието със същия API, който използвахте, за да ги създадете първия път. Понеже ги създавате наново спрямо собствените диапазони на дубликата, препратките излизат правилни по конструкция. За диаграма, която чете A1:C10, добавете нова диаграма върху копието, насочена към A1:C10 на копието; за автофилтър, който искате да остане действащ, имайте предвид, че диапазонът на филтъра се пренася, така че трябва само да приложите отново критериите за колоните. Правилата за условно форматиране и валидации на данни бихте добавили наново чрез същите извиквания, описани в статията за обединени клетки и оформление на шаблони за отчети, която обхожда таблицата с обединенията и модела на диапазоните, които копието наследява

Диаграма, разделяща дублирането на HotXLS XLSX работен лист в Delphi на състоянието на оформление, което CopyFrom копира дълбоко, и диаграмите, таблиците и правилата с препратки, оставени на повикващия за възстановяване
Layout, print настройка и защита се копират дълбоко чисто; chart-ове, таблици и правила носят референции, които трябва да се възстановят

Къде се вписва дублирането в процеса за генериране на отчети

Дублирането на работни листове е естественият спътник на генерирането, управлявано от заместители. Подходът, закотвен за маркери, в ръководството за генериране на отчети от шаблони в Delphi, решава проблема да пишете данни в оформление, което други хора редактират; дублирането решава проблема да ви трябва това оформление много пъти в една работна книга. Комбинирайте ги и моделът е чист: дръжте един незасегнат лист Template с неговите маркери, обединения и настройка за печат, след което за всеки регион или период извиквайте Duplicate, попълнете маркерите на клона със съответното парче данни и продължете нататък. Незасегнатият шаблон никога не се променя, така че остава надежден източник за следващия клон, а всеки изходен лист тръгва от байт-в-байт идентично оформление

Една бележка за последователността спестява цяла категория обърквания. Дублирайте листа преди да налеете данни в него, а не след това. Шаблонът трябва да носи структура и форматиране, а не цифрите от миналото тримесечие, а клонирането на празен, но стилизиран лист означава, че всеки дубликат тръгва чист. Ако дублирате лист, който вече носи данни, тези данни идват заедно с него, защото CopyFrom копира използвания диапазон вярно; понякога точно това искате, но за разклонен отчет обикновено не е така

Бърз навик за проверка

Понеже разделянето между дълбоко и плитко копиране е невидимо, докато не го потърсите, вградете петредова проверка в задачата, вместо да разчитате, че всичко е преминало. След дублирането прочетете обратно структурните сигнали, които клонът трябва да наследи, и проверете дали съвпадат с източника

Copy := Book.Sheets.Duplicate(1, 'Region-North');
WriteLn(Format('merged=%d  colA=%.1f  freezeRow=%d  tabAuto=%d',
  [Copy.MergedCells.Count, Copy.ColWidth[1],
   Copy.FreezeRow, Integer(Copy.TabColorIsAuto)]));
// Докажете независимостта: променете копието и проверете, че източникът е незасегнат.
Copy.Cells[2, 2].Value := 999;
// Template.Cells[2, 2].Value продължава да е това, което е било.

Броят на обединенията, ширината на една колона, закрепеният ред и флагът за цвета на езичето ви казват, че пластът, който се копира, наистина е бил пренесен. Отделно, във всеки лист, който е носил диаграма, таблица, валидации или условни формати, третирайте ги като списък за възстановяване върху копието: тяхното отсъствие е по замисъл, а поправката е няколко извиквания, не доклад за бъг. Този умствен модел, дълбоко там, където е безопасно, и плитко там, където препратките биха се счупили, е цялата история за това как да използвате тази функция добре

Дублирането на работни листове и копирането на състоянието на листа чрез CopyFrom, описани тук, се доставят във v2.91.0 на нативния HotXLS Delphi компонент за електронни таблици, заедно с изпълним пример SheetCopy, който упражнява цикъла клонирай-и-промени от край до край

Диаграма на HotXLS конвейер за отчети в Delphi, дублиращ един чист стилизиран XLSX шаблонен лист в клонините по регион, всеки запълван със собствени данни само след клониране
Пазете шаблона непокътнат, дублирайте го на регион и наливайте данни само след клонирането