Has construido una hoja exactamente como debe ser. La banda de encabezado está combinada, los anchos de columna encajan con los datos, las dos primeras filas están inmovilizadas, el área de impresión y los márgenes están listos para una exportación A4 limpia, y la pestaña tiene color para que finanzas la encuentre. Ahora el informe necesita doce copias de esa hoja, una por región, todas partiendo del mismo diseño. Volver a construir esa hoja doce veces en código es la forma en que se cuela la deriva sutil: la región 7 acaba con una columna un punto más estrecha, la región 11 pierde la inmovilización y nadie se da cuenta hasta que el PDF llega al escritorio de un directivo. Lo que en realidad quieres es la versión programática del clic derecho de Excel, Mover o copiar, Crear una copia: tomar la hoja terminada y generar duplicados independientes
El motor XLSX de HotXLS, una biblioteca nativa para Delphi y C++Builder que lee y escribe archivos de Excel sin automatizar el propio Excel, ya podía mover hojas, eliminar hojas y copiar rangos de celdas entre hojas. Lo que no podía hacer hasta la v2.91.0 era clonar una hoja completa de una sola llamada. Esa versión añade dos puntos de entrada: TXLSXWorksheet.CopyFrom, que copia el estado de una hoja a otra, y TXLSXSheets.Duplicate, que añade una nueva hoja y ejecuta CopyFrom por ti. La parte interesante no es que copie cosas, sino la línea deliberada que separa lo que se copia en profundidad y lo que no, y por qué esa línea está justo ahí
Una sola llamada para clonar una hoja terminada
La operación de alto nivel es Duplicate. Pásale el índice basado en 1 de la hoja de origen y devolverá una hoja nueva que refleja el diseño y los datos del original. La convención de índice coincide con Items[] en la parte XLSX, así que la primera hoja es el índice 1, no 0; si pasas un índice fuera de rango obtendrás nil en lugar de una excepción, el mismo contrato de error que usa el resto de la colección de hojas 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;
Hay dos cosas en ese fragmento que merecen detenerse un momento. Primera, FreezePanes recibe los argumentos primero por fila, FreezePanes(ARow, ACol), así que encaja con la indexación Cells[Row, Col]; la copia hereda exactamente la misma división de congelación. Segunda, el método se llama Duplicate y no el más obvio Copy, y eso no es una cuestión de estilo. Copy es una rutina estándar de la unidad System, usada constantemente para cadenas y arrays dinámicos. Un método llamado Copy en una clase lo ocultaría dentro de los cuerpos de método y crearía exactamente la clase de ambigüedad de resolución que te muerde seis meses después. Duplicate esquiva todo ese problema y se lee bien en el punto de llamada
El nombre predeterminado sigue la regla de Excel
Cuando llamas a la sobrecarga de un argumento o pasas una cadena de nombre vacía, la nueva hoja se nombra a partir de la hoja de origen con un sufijo (2), y el sufijo va aumentando hasta que el nombre es único. Duplica la hoja Template una vez y obtienes Template (2); duplícala otra vez y obtienes Template (3), porque Template (2) ya está ocupada. Eso refleja los nombres que Excel genera con su propio comando Crear una copia, de modo que un libro producido por tu código se vea como esperaría un usuario que lo hubiera duplicado a mano. La comprobación de unicidad se ejecuta contra la colección de hojas en vivo, así que también salta por encima de nombres que hayas creado manualmente, no solo de duplicaciones anteriores
Si estás generando una hoja por región o por mes, apóyate en la sobrecarga con nombre explícito. Un esquema predecible Region-North, Region-South es más fácil de direccionar después que una ristra de sufijos (2), (3), y además mantiene legibles tus nombres definidos y las fórmulas entre hojas
Qué copia en profundidad CopyFrom
Por debajo, Duplicate añade la hoja y luego llama a CopyFrom(ASource), que también puedes llamar directamente cuando quieras clonar sobre una hoja que ya hayas creado. CopyFrom se protege de entrada frente a los dos casos degenerados: copiar desde nil o copiar una hoja sobre sí misma; en ambos casos devuelve de inmediato y no hace nada. Todo lo que viene después ya es la copia en sí, y es deliberadamente amplia
Los datos de celda van primero. CopyFrom pide a la fuente su UsedRange, el rectángulo mínimo que encierra las celdas con contenido y las regiones combinadas, y reutiliza la maquinaria existente CopyRangeTo para llevar cada valor, fórmula e índice de estilo por celda al destino empezando en A1. Encima de las celdas, reproduce toda la capa de estado de hoja que hace que una plantilla parezca terminada:
- Rangos combinados, recreados por coordenadas para que el banner ocupe el mismo rectángulo
- Anchos de columna y alturas de fila, además de las listas de ocultación, plegado y nivel de esquema, copiadas tal cual para que las filas y columnas no predeterminadas queden alineadas exactamente
- Paneles inmovilizados y el estado de vista: nivel de zoom, visualización de líneas de cuadrícula y valores cero, dirección de derecha a izquierda y tipo de vista
- Estado de protección con sus opciones de permiso por acción, para que una plantilla bloqueada siga bloqueada igual
- Todo el bloque de configuración de página: márgenes, orientación, tamaño de papel, escalado y ajuste a página, área de impresión, títulos de impresión, encabezados y pies de página, y las marcas de líneas de cuadrícula y encabezados en impresión
- El rango de AutoFilter, el color de la pestaña y la visibilidad de la hoja
El resultado es una hoja que imprime, filtra y se presenta igual que la original. Y como las celdas, los rangos combinados y las listas de dimensiones se recrean físicamente en la hoja nueva en lugar de quedar aliasadas, la copia es totalmente independiente. Escribe 999 en una celda de la copia y la hoja original conserva su valor original; esa independencia es la propiedad más importante de una copia pensada para informes regionales en paralelo, y la demo SheetCopy incluida lo comprueba explícitamente
Qué deja sin copiar y por qué
Ahora la parte honesta. Los gráficos, las imágenes incrustadas, las tablas XLSX, las validaciones de datos y las reglas de formato condicional no se copian. Este es un límite documentado y deliberado, no una omisión, y conviene entender la razón para poder planificar en torno a él en lugar de que te pille por sorpresa
Cada una de esas colecciones arrastra identidad y referencias que no sobreviven a una copia ingenua de campos. Un gráfico apunta a un rango de datos de origen y posee una relación de dibujo en el paquete OOXML; clonar el objeto sin reasignar la relación y las referencias de las series produce un gráfico que se renderiza contra los datos equivocados, o un paquete que Excel marca como necesitado de reparación. Una tabla tiene un nombre que debe ser único dentro del libro, una fila de encabezado vinculada a columnas concretas y su propia relación generada automáticamente. Los formatos condicionales y las validaciones de datos se adhieren a rangos de coordenadas y, en el caso de las validaciones, pueden referenciar otros rangos mediante fórmula. Copiar en profundidad cualquiera de estas cosas correctamente implica reescribir referencias y asignar identidades nuevas, que es trabajo real con modos reales de fallo. Hacerlo a medias, copiando el objeto pero no sus referencias, es peor que no copiar nada: genera un archivo que se abre con un aviso de reparación y descarta contenido en silencio. Así que el motor copia lo que puede copiar limpiamente y deja las colecciones con referencias al llamador, que sabe a qué debe apuntar el destino
En la práctica eso significa que el flujo para una plantilla más rica es: duplicar la hoja para obtener las celdas, el diseño y la configuración de impresión, y luego reconstruir el gráfico, la tabla, las validaciones o los formatos condicionales en la copia con la misma API que usaste para crearlos la primera vez. Como los estás recreando contra los rangos propios de la copia, las referencias salen correctas por construcción. Para un gráfico que lea A1:C10, añade un gráfico nuevo en la copia apuntando a A1:C10 de la copia; para un AutoFilter que quieras activo, ten en cuenta que el rango del filtro sí se conserva, así que solo necesitas volver a aplicar los criterios de columna. Las reglas de formato condicional y validación de datos se volverían a añadir mediante las mismas llamadas descritas en el artículo sobre celdas combinadas y el diseño de plantillas de informe, que recorre la tabla de combinaciones y el modelo de rangos que hereda la copia
Dónde encaja la duplicación en un flujo de informes
La duplicación de hojas es la compañera natural de la generación basada en marcadores. El enfoque anclado en tokens de la guía para la generación de informes a partir de plantillas en Delphi resuelve el problema de escribir datos en un diseño que otros editan; la duplicación resuelve el problema de necesitar ese diseño muchas veces en un mismo libro. Combínalos y el patrón queda limpio: conserva una hoja Template impecable con sus tokens, combinaciones y configuración de impresión, y para cada región o periodo llama a Duplicate, rellena los tokens de la copia con ese fragmento de datos y sigue adelante. La plantilla impecable nunca se modifica, así que sigue siendo una fuente fiable para la siguiente copia, y cada hoja de salida parte de un diseño idéntico bit a bit
Una nota de secuencia evita una clase de confusión. Duplica la hoja antes de volcar datos en ella, no después. Una plantilla debe contener estructura y formato, no los números del trimestre pasado, y clonar una hoja con estilo vacía significa que cada copia empieza limpia. Si duplicas una hoja que ya lleva datos, esos datos viajan con ella, porque CopyFrom copia fielmente el UsedRange; a veces eso es justo lo que quieres, pero para un informe distribuido en varias hojas normalmente no
Un hábito rápido de verificación
Como la división entre copia profunda y copia superficial es invisible hasta que la buscas, incorpora una comprobación de cinco líneas en el trabajo en vez de confiar en que todo se haya copiado. Después de duplicar, lee de vuelta las señales estructurales que se supone que la copia debe heredar y comprueba que coinciden con la fuente
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.
El recuento de combinaciones, un ancho de columna, la fila de congelación y el indicador de color de pestaña te dicen que la capa que sí se copia realmente ha llegado. Por separado, en cualquier hoja que llevara un gráfico, una tabla, validaciones o formatos condicionales, trátalos como una lista para reconstruir en la copia: su ausencia es deliberada, y la solución son unas pocas llamadas, no un informe de error. Ese modelo mental, copia profunda donde es seguro y copia superficial donde las referencias se romperían, es toda la historia de cómo usar bien esta funcionalidad
La duplicación de hojas y la copia del estado de hoja mediante CopyFrom que se describen aquí llegan en la v2.91.0 del componente nativo HotXLS Delphi spreadsheet component, junto con una demo ejecutable SheetCopy que recorre de principio a fin el ciclo de clonar y mutar