Artículo técnico

Validación de datos, AutoFilter y tablas de hoja en Delphi con HotXLS

Un servicio batch aplicó un AutoFilter para descartar filas rechazadas de un libro de pedidos y luego entregó el archivo a una segunda etapa que lo leyó de vuelta y exportó los datos "filtrados". Todas las filas rechazadas pasaron. El código era correcto según las reglas de la API y equivocado según el modelo mental del desarrollador: un AutoFilter en un libro guardado es una definición almacenada, y el ocultamiento de filas ocurre cuando Excel abre el archivo y evalúa los criterios. HotXLS escribe la definición fielmente; no recorta, oculta ni elimina nada. Entender cuál de estas tres funciones cambia datos, cuál cambia presentación y cuál solo registra intención es el núcleo de generar desde Delphi libros editables que se comporten en Excel igual que en sus pruebas.

AutoFilter: intención almacenada, evaluada en otro lugar

En la hoja XLSX, SetAutoFilter declara la región filtrada y AddAutoFilterColumn adjunta criterios a una columna de esa región. Cuando el código del servidor necesita saber el resultado, para un conteo de filas en un resumen o para pasar solo filas coincidentes a la siguiente etapa, la biblioteca proporciona la evaluación explícitamente en lugar de fingir que el archivo cambió:

var
  Book: TXLSXWorkbook;
  Sheet: TXLSXWorksheet;
  R, Visible: Integer;
begin
  Book := TXLSXWorkbook.Create;
  try
    Book.Open('orders.xlsx');
    Sheet := Book.Sheets[0];

    Sheet.SetAutoFilter('A1:E500');
    // Column id 3 = fourth column INSIDE the filter range (0-based offset)
    Sheet.AddAutoFilterColumn(3, xlsxAfOpGreaterOrEqual, '1000');

    Visible := 0;
    for R := 2 to 500 do
      if Sheet.AutoFilterRowVisible(R) then
        Inc(Visible);
    // Visible now matches what Excel will show after opening the file

    Book.SaveAs('orders-filtered.xlsx');
  finally
    Book.Free;
  end;
end;

AutoFilterRowVisible responde por fila, y PreviewAutoFilterRows recorre toda la región mediante un callback cuando necesitan el conjunto coincidente en una sola pasada. Si el requisito de negocio es que las filas excluidas no estén en el archivo en absoluto, un recorte de privacidad y no una vista, eliminen las filas; un filtro es la herramienta equivocada, porque cualquier destinatario puede limpiarlo con un clic.

El ID de columna es un desplazamiento, no un número de columna

El comentario del fragmento anterior marca el segundo error clásico en esta API. AddAutoFilterColumn identifica su destino por la posición 0-based dentro del rango filtrado, no por la columna de la hoja. Para un filtro sobre A1:E500 los dos sistemas difieren apenas en uno, exactamente el tipo de casi acierto que sobrevive una prueba rápida; para un filtro que empieza en la columna C, id 0 significa columna C y el error se vuelve obvio. Cuando el rango de filtro se calcula en tiempo de ejecución, deriven el ID de columna de la misma variable que construyó la cadena de rango, nunca de una constante de columna de hoja. Cada columna acepta una segunda condición mediante el overload que recibe dos operadores, dos criterios y un conector and/or, reflejando el cuadro de filtro personalizado de Excel. La fachada XLS cubre el mismo terreno con SetAutoFilter más ApplyAutoFilter, cuyos parámetros de criterio y operador siguen las convenciones COM más antiguas, incluido un número de campo 1-based; cambiar entre fachadas significa cambiar bases de índice, y vale la pena dejar un comentario en el punto de llamada.

Reglas de validación: el contrato bajo el que editan sus usuarios

La validación de datos es la única de las tres funciones que restringe activamente la entrada futura, y merece la mayor atención de diseño en libros que salen para ser completados y vuelven para procesamiento. La variante de lista es el caballo de batalla:

var
  Idx: Integer;
begin
  Idx := Sheet.AddListValidation('C2:C500', 'New,Approved,Blocked');
  Sheet.DataValidations[Idx].SetPrompt('Status',
    'Pick one of the listed states');
  Sheet.DataValidations[Idx].SetError('Invalid status',
    'Type or paste only listed values', xlsxDvErrStop);
  Sheet.DataValidations[Idx].AllowBlank := False;

  // Quantities: whole numbers, zero or more
  Sheet.AddWholeNumberValidation('D2:D500', xlsxDvOpGreaterOrEqual, '0');
end;

Más allá de listas y números enteros, la misma familia cubre decimales, fechas, horas, longitud de texto y fórmulas libres mediante AddCustomValidation, con AddDataValidation genérico exponiendo toda la matriz de tipo y operador para constructores de reglas impulsados por configuración. El estilo de error importa más de lo que parece: xlsxDvErrStop rechaza directamente la entrada inválida, mientras que los estilos de advertencia e información dejan pasar el valor después de un clic; elijan por columna según si el código aguas abajo puede tolerar sorpresas. Documenten dos límites para sus usuarios: la validación en Excel protege la escritura, pero pegar un bloque sobre un rango validado puede saltarse la revisión, así que el código de lectura aún debe validar; y una regla cubre el rango literal que ustedes le dieron, por lo que aplicar validación antes de saber el conteo final de filas deja la cola agregada sin protección. Escriban los datos y luego adjunten reglas dimensionadas al alcance real.

La fachada heredada ofrece las mismas familias de reglas con una diferencia ergonómica: los creadores del lado XLS, AddWholeNumberValidation, AddDecimalValidation, AddDateValidation, AddTimeValidation, AddTextLengthValidation y AddCustomValidation, devuelven directamente el objeto TDataValidation en lugar de un índice, así que la configuración de prompt y error se encadena sobre la referencia devuelta. La enumeración de operadores (xlsDvBetween, xlsDvGreaterThan y las demás) refleja el conjunto XLSX, lo que mantiene el código constructor de reglas portable entre fachadas aparte de la diferencia de estilo de retorno. De cualquier modo, el texto de prompt merece tanto cuidado como la regla misma: un desplegable que rechaza entradas con un cuadro de error en blanco entrena a los usuarios a llamar a IT, mientras que uno que dice exactamente qué estados son válidos los entrena a corregirlo por sí mismos.

Una inversión de polaridad que la biblioteca absorbe por ustedes

Cualquiera que haya leído XML de validación OOXML a mano sabe que el atributo showDropDown está invertido: en ISO/IEC 29500 significa "suprimir la flecha desplegable". HotXLS invierte esto internamente para que la propiedad ShowDropDown en una regla de validación signifique lo que dice: true muestra el desplegable. La única forma de quemarse es mezclar niveles: establecer la propiedad desde código mientras un colega audita el XML guardado y "corrige" el atributo aparentemente incorrecto. Elijan un solo nivel de verdad para las herramientas de revisión y documenten allí la inversión.

Tablas: dar a un rango un esquema y un nombre

Una tabla de hoja (ListObject) envuelve un rango con nombre, columnas tipadas, estilo con bandas y soporte de referencias estructuradas, y es lo que hace que un libro generado se sienta terminado cuando los usuarios ordenan y lo extienden. La creación es simétrica entre fachadas: AddTable recibe un nombre, un rango y una lista de columnas:

var
  Cols: TStringList;
begin
  Cols := TStringList.Create;
  try
    Cols.CommaText := 'OrderId,Customer,Status,Amount,Owner';
    Sheet.AddTable('Orders', 'A1:E500', Cols);
  finally
    Cols.Free;
  end;
end;

Del lado XLSX, el objeto tabla resultante expone StyleName (la familia integrada TableStyleMedium2 y sus hermanas), toggles de bandas y una bandera de fila de totales, así que el estilo corporativo es una asignación de propiedad y no una pasada de formato. En archivos .xls heredados, la misma llamada escribe los registros de tabla BIFF8, y la fachada además ofrece AddPivotTable para vistas de resumen con campos de fila, columna y datos, un recordatorio de que las "tablas" en el formato heredado son una maquinaria más rica y antigua que el ListObject de OOXML. Nombren tablas como nombran vistas de base de datos: el código aguas abajo que lee Orders[Amount] por referencia estructurada sobrevive a reordenamientos de columnas que el código posicional no tolera.

Dos convenciones ahorran limpieza después. Excel exige que los nombres de tabla sean únicos en todo el libro, así que un generador que emite una hoja por región necesita un esquema como Orders_EMEA en lugar de reutilizar Orders; el duplicado no falla al escribir, falla en el diálogo de reparación de Excel. Y cuando la fila de totales está habilitada, recuerden que se ubica debajo del rango de datos, así que cualquier código que luego agregue filas con "última fila usada más uno" escribirá dentro de la banda de totales; registren el alcance de datos separado del alcance de la tabla.

Las tres funciones se componen naturalmente en entregables de captura de datos: una tabla define la región editable, la validación restringe las columnas que los usuarios llenan y un filtro preconfigurado ahorra al destinatario los primeros tres clics. La mitad anterior de ese pipeline, llevar resultados de consultas a la hoja de forma eficiente, es el tema de exportar resultados de base de datos a Excel desde Delphi, y los libros donde fórmulas resumen los datos validados se benefician de nombres definidos para referencias estables entre hojas.

FAQ: comportamiento de validación y filtrado

¿Por qué las filas filtradas siguen en mis datos exportados? Porque un filtro es una definición evaluada por la aplicación que abre el archivo. Usen AutoFilterRowVisible o PreviewAutoFilterRows para aplicar los criterios en su propio código, o eliminen las filas que no deben enviarse.

¿Una validación de lista impide que los usuarios peguen valores inválidos? No. El pegado de Excel puede sobrescribir celdas validadas sin disparar la regla. Traten la validación como guía para quienes escriben y mantengan verificaciones del lado servidor sobre todo lo que lean de vuelta.

¿Puedo preaplicar un filtro para que el libro se abra ya filtrado? Sí: escriban los criterios con AddAutoFilterColumn y guarden; Excel evalúa y oculta las filas que no coinciden al abrir. Solo recuerden que las filas permanecen presentes en el archivo.

La validación, los filtros y las tablas son la diferencia entre enviar una cuadrícula de valores y enviar una pequeña aplicación. La referencia completa de reglas, filtros y tablas está en la página de producto HotXLS Component.