Artigo Técnico

Duplicar uma folha XLSX no Delphi com HotXLS

Você montou uma folha de cálculo exatamente do jeito certo. A faixa do cabeçalho está mesclada, as larguras das colunas se ajustam aos dados, as duas primeiras linhas estão congeladas, a área de impressão e as margens foram definidas para uma exportação A4 limpa, e a guia está colorida para o financeiro encontrá-la. Agora o relatório necessita de doze dessas, uma por região, todas começando do mesmo layout. Recriar essa folha de cálculo em código doze vezes é o tipo de coisa que faz surgir desvios sutis: a região 7 ganha uma coluna um ponto mais estreita, a região 11 perde o congelamento, e ninguém percebe até o PDF chegar à mesa de um gerente. O que você realmente quer é a versão programática do clique direito do Excel, Mover ou Copiar, Criar uma cópia: pegue a folha de cálculo pronta e gere duplicatas independentes

O mecanismo XLSX do HotXLS, uma biblioteca nativa de Delphi e C++Builder que lê e grava ficheiros Excel sem automatizar o Excel em si, já conseguia mover folhas de cálculo, excluir folhas de cálculo e copiar intervalos de células entre folhas de cálculo. O que ele não fazia até a v2.91.0 era clonar uma folha de cálculo inteira em uma única chamada. Essa versão acrescenta dois pontos de entrada: TXLSXWorksheet.CopyFrom, que copia o estado da folha de cálculo de uma worksheet para outra, e TXLSXSheets.Duplicate, que cria uma nova folha de cálculo e executa CopyFrom para você. O interessante não é que ele copia coisas. É a linha deliberada entre o que é copiado em profundidade e o que não é, e por que essa linha fica exatamente ali

Uma chamada para clonar uma folha de cálculo pronta

A operação de alto nível é Duplicate. Passe o índice 1-based da folha de cálculo de origem e ele retorna uma worksheet totalmente nova, espelhando o layout e os dados da original. A convenção de índice corresponde a Items[] no lado XLSX, então a primeira folha de cálculo tem índice 1, não 0; se você passar um índice fora do intervalo, recebe nil em vez de uma exceção, o mesmo contrato de falha usado pelo restante da coleção de folhas de cálculo 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);          // freeze top row + first column
    Template.TabColorIsAuto := False;
    Template.TabColor := $FF1F4E79;

    // Clone with an explicit name...
    Copy := Book.Sheets.Duplicate(1, 'Region-North');
    // ...or let it pick the Excel-style default name.
    Copy := Book.Sheets.Duplicate(1);    // -> "Template (2)"

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

Duas coisas nesse trecho merecem atenção. Primeiro, FreezePanes recebe os argumentos na ordem linha primeiro, FreezePanes(ARow, ACol), então ele se alinha ao indexador Cells[Row, Col]; a cópia herda exatamente a mesma divisão congelada. Segundo, o método se chama Duplicate e não o mais óbvio Copy, e isso não é preferência de estilo. Copy é uma rotina predefinido na unit System, usada o tempo todo com strings e arrays dinâmicos. Um método chamado Copy em uma classe o sombrearia dentro dos corpos dos métodos e criaria exatamente o tipo de ambiguidade de resolução que cobra a conta meses depois. Duplicate contorna o problema inteiro e faz sentido no ponto de chamada

O nome predefinido segue a própria regra do Excel

Quando você chama a sobrecarga com um argumento, ou passa uma string de nome vazia, a nova folha de cálculo recebe o nome da origem com o sufixo (2), e esse sufixo avança até o nome ficar único. Duplique a folha de cálculo Template uma vez e você obtém Template (2); duplique de novo e recebe Template (3), porque Template (2) já está ocupado. Isso espelha os nomes que o Excel gera com o próprio comando Criar uma cópia, então uma pasta de trabalho produzida pelo seu código fica com a aparência que um utilizador esperaria de uma duplicação manual. A verificação de unicidade atua sobre a coleção de folhas de cálculo em uso, o que significa que ela também respeita nomes criados manualmente, não só os vindos de duplicações anteriores

Se você está gerando uma folha de cálculo por região ou por mês, prefira a sobrecarga com nome explícito. Um esquema previsível como Region-North e Region-South é mais fácil de endereçar depois do que uma sequência de sufixos (2), (3), e mantém seus nomes definidos e fórmulas entre folhas de cálculo legíveis

O que CopyFrom copia em profundidade

Por baixo dos panos, Duplicate adiciona a folha de cálculo e então chama CopyFrom(ASource), que você também pode chamar diretamente quando quiser clonar sobre uma folha de cálculo já criada. CopyFrom protege de antemão os dois casos degenerados: copiar de nil ou copiar uma folha de cálculo sobre ela mesma. Em ambos os casos ele retorna imediatamente e não faz nada. Tudo depois disso é a cópia em si, e ela é deliberadamente ampla

Os dados das células vêm primeiro. CopyFrom pede à origem o UsedRange, o retângulo justo que contém as células preenchidas e as regiões mescladas, e reaproveita a infraestrutura existente de CopyRangeTo para levar todos os valores, fórmulas e índices de estilo de cada célula para o destino, começando em A1. Sobre as células, ele refaz toda a camada de estado da folha de cálculo que faz um modelo parecer finalizado:

  • Regiões mescladas, recriadas por coordenada para que o banner ocupe o mesmo retângulo
  • Larguras de coluna e alturas de linha, além das listas de oculto, recolhido e nível de estrutura, copiadas literalmente para que linhas e colunas fora do predefinido se alinhem com precisão
  • Congelamento de painéis e estado de visualização: nível de zoom, exibição de linhas de grade e de zeros, direção da direita para a esquerda e tipo de exibição
  • Estado de proteção com suas opções de permissão por ação, para que um modelo bloqueado permaneça bloqueado do mesmo jeito
  • Todo o bloco de configuração de página: margens, orientação, tamanho do papel, escala e ajuste à página, área de impressão, títulos de impressão, cabeçalhos e rodapés, além das flags de linhas de grade e títulos de impressão
  • Intervalo de AutoFilter, cor da guia e visibilidade da folha de cálculo

O resultado é uma folha de cálculo que imprime, filtra e se apresenta exatamente como a origem. E, como as células, as mesclagens e as listas de dimensões são recriadas fisicamente na nova folha de cálculo, e não apenas apontadas para a mesma memória, a cópia é totalmente independente. Escreva 999 em uma célula da cópia e a origem mantém o valor original; essa independência é a propriedade mais importante de um clone pensado para relatórios regionais em paralelo, e o demo SheetCopy já a verifica explicitamente

O que fica superficial, e por quê

Agora a parte honesta. Gráficos, imagens incorporadas, tabelas XLSX, validações de dados e regras de formatação condicional não são copiados. Essa é uma fronteira documentada e deliberada, não uma omissão, e vale entender o motivo para planejar em torno dela em vez de ser surpreendido

Cada uma dessas coleções carrega identidade e referências que não sobrevivem a uma cópia ingênua de campos. Um gráfico aponta para um intervalo de dados de origem e possui uma relação de desenho no pacote OOXML; clonar o objeto sem remapear a relação e as referências das séries produz um gráfico desenhado com os dados errados, ou um pacote que o Excel marca como necessitando reparo. Uma tabela tem um nome que necessita ser único dentro da pasta de trabalho, uma linha de cabeçalho ligada a colunas específicas e sua própria relação gerada automaticamente. Formatações condicionais e validações de dados se prendem a intervalos de coordenadas e, no caso da validação, podem referenciar outros intervalos por fórmula. Copiar em profundidade qualquer uma delas corretamente significa reescrever referências e gerar identidades novas, o que é trabalho real com modos reais de falha. Fazer isso pela metade, copiando o objeto mas não suas referências, é pior do que não copiar nada: resulta em um ficheiro que abre com pedido de reparo e descarta conteúdo em silêncio. Então o mecanismo copia o que consegue copiar de maneira limpa e deixa as coleções que carregam referência para o aplicação chamadora, que sabe para onde o destino deve apontar

Na prática, isso significa que o fluxo para um modelo mais rico é: duplique a folha de cálculo para obter células, layout e configuração de impressão, depois recrie o gráfico, a tabela, as validações ou as formatações condicionais na cópia com a mesma API usada na primeira criação. Como você está recriando esses elementos sobre os intervalos da própria cópia, as referências saem corretas por construção. Para um gráfico que lê A1:C10, adicione um novo gráfico na cópia apontando para o A1:C10 da cópia; para um AutoFilter que você quer manter ativo, observe que o intervalo do filtro é preservado, então você só reaplica os critérios de coluna. As regras de formatação condicional e validação de dados que você reaplicaria pelas mesmas chamadas descritas no artigo sobre layout de células mescladas e modelos de relatório usam a tabela de mesclagem e o modelo de intervalo que a cópia herda

Onde a duplicação entra no pipeline de relatórios

A duplicação de folhas de cálculo é a companheira natural da geração guiada por marcadores. A abordagem ancorada em tokens no guia de geração de relatórios a partir de modelos no Delphi resolve o problema de escrever dados em um layout que outras pessoas editam; a duplicação resolve o problema de precisar desse layout várias vezes em uma única pasta de trabalho. Combine as duas e o predefinido fica limpo: mantenha uma folha de cálculo Template intacta, com seus tokens, mesclagens e configuração de impressão, depois, para cada região ou período, chame Duplicate, preencha os tokens da cópia com aquele recorte de dados e siga adiante. O modelo original nunca é modificado, então ele continua sendo uma fonte confiável para a próxima cópia, e cada folha de cálculo de saída inicia com um layout idêntico byte a byte

Uma observação de sequência evita confusão. Duplique a folha de cálculo antes de despejar dados nela, não depois. Um modelo deve guardar estrutura e formatação, não os números do trimestre anterior, e clonar uma folha de cálculo vazia e estilizada significa que cada cópia inicia limpa. Se você duplica uma folha de cálculo que já contém dados, esses dados vêm junto, porque CopyFrom copia fielmente o intervalo usado; às vezes é isso que você quer, mas, para um relatório em fan-out, normalmente não é

Um hábito rápido de verificação

Como a divisão entre cópia profunda e superficial fica invisível até que alguém a procure, coloque uma checagem de cinco linhas no processo em vez de confiar que tudo foi preservado. Depois de duplicar, leia de volta os sinais estruturais que a cópia deveria herdar e confirme que correspondem à origem

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)]));
// Prove independence: mutate the copy, confirm the source is untouched.
Copy.Cells[2, 2].Value := 999;
// Template.Cells[2, 2].Value is still whatever it was.

The merge count, a column width, the freeze row, and the tab-colour flag tell you the layer that is copied actually made it. Separately, in any sheet that carried a chart, a table, validations, or conditional formats, treat those as a to-rebuild list on the copy: their absence is by design, and the fix is a few calls, not a bug report. That mental model, deep where it is safe and shallow where references would break, is the whole story of how to use this feature well

A duplicação de folhas de cálculo e a cópia de estado de folha de cálculo via CopyFrom descritas aqui fazem parte da v2.91.0 do componente de folhas de cálculo HotXLS para Delphi, junto com um exemplo executável SheetCopy que exercita o ciclo de clonar e alterar do início ao fim