Esta página puede contener texto traducido automáticamente.

Contenedor Table

Utilice contenedores Table para diseñar sus datos más complejos. Las tablas desempeñan un papel crucial en los documentos PDF, mejorando su claridad y eficacia.

Las tablas proporcionan un formato estructurado para presentar sus datos. Le permiten presentar detalles complejos de una manera concisa y visualmente atractiva. En lugar de párrafos extensos, puede utilizar tablas para presentar hechos, cifras y tendencias de manera sucinta.

Una tabla bien diseñada mejora la legibilidad. Al alinear columnas y filas, se crea una estructura que guía la mirada del lector. Puede utilizar fondos, bordes y estilos de fuente para enfatizar celdas o encabezados específicos.

Contenedor Table

Obtenga un contenedor de tabla llamando al método LayoutContainer.Table. Luego debes definir al menos una columna en el contenedor. En el caso más simple, puedes llenar todas las columnas y filas llamando al método Cell varias veces.

Continúe leyendo para saber cómo definir columnas, agregar un encabezado y/o pie de página a una tabla. El texto también describe todas las demás características del contenedor Table.

Este artículo es parte de una serie sobre la API Layout para la generación de PDF. Si es nuevo en la API, lea primero la parte Introducción a la API de Layout.

Biblioteca Docotic.Pdf 9.5.17573-dev Complemento de diseño 9.5.17573-dev
Pruebas de regresión 14,726 pasaron Descargas totales de NuGet 4,765,230

Columnas y filas

Cada tabla debe definir al menos una columna. Utilice el método Table.Columns para definir columnas. El método acepta un delegado de tipo Action<TableColumnContainer>. En el código delegado, agregue columnas a la tabla llamando a los métodos del objeto proporcionado.

El método ConstantColumn agrega una columna con un ancho igual a un número exacto de puntos.

El método RelativeColumn agrega una columna con un ancho relativo. El método acepta la cantidad de partes que debe ocupar la columna. El número total de partes es la suma de todos los números en todas las llamadas a RelativeColumn en esta tabla. Básicamente, este método funciona igual que el RelativeItem del contenedor Row. Lea el enlace para obtener una explicación detallada.

No es necesario definir filas explícitamente. El número de filas depende de las propiedades de la columna y la celda. El número de celdas también puede afectar el número de filas. La tabla puede tener una altura mayor que la suma de las alturas de todas sus filas. Las filas pueden tener menos celdas que el número de columnas. Y es posible que las columnas tengan menos celdas que el número de filas.

PdfDocumentBuilder.Create().Generate("table-basic.pdf", doc =>
{
    doc.Pages(page =>
    {
        page.Size(300, 100);
        page.Content()
            .Padding(10)

            //.MinimalBox()

            .Border(b => b.Thickness(1).Color(new PdfRgbColor(250, 123, 5)))
            .Table(table =>
            {
                table.Columns(columns =>
                {
                    columns.ConstantColumn(100);
                    columns.RelativeColumn(1);
                    columns.RelativeColumn(3);
                });

                for (int i = 0; i < 10; i++)
                {
                    table.Cell()
                        .Border(b => b.Thickness(0.1))
                        .AlignCenter()
                        .Text($"Cell {i + 1}");
                }
            });
    });
});

Puede ver el resultado del código en table-basic.pdf.

En el PDF resultante, la tabla ocupa todo el espacio del contenedor principal. Aunque no hay suficientes celdas para cubrir el área. Descomente la llamada MinimalBox para limitar la altura de la tabla a la suma de las alturas de sus filas.

El código define tres columnas y agrega 10 celdas a la tabla. La tabla coloca las celdas una tras otra y agrega una nueva fila cuando es necesario. Está perfectamente bien tener una fila con una sola celda.

La tabla colocará celdas en la página siguiente si la página actual no tiene suficiente espacio. Puede observar esto aumentando el número de celdas agregadas.

Células

Permítanme explicarles qué proporciona Layout API para trabajar con celdas de tablas.

Posición

Hay dos formas de crear una celda de tabla:

  • sin información de posición explícita
  • con posición al menos parcialmente especificada

Utilicé la primera forma en el código de la sección anterior. Llamas a la sobrecarga del método Cell que no acepta parámetros. El método agrega una nueva celda a la columna después de la última celda agregada. Si no hay espacio a la derecha, la celda pasa a la siguiente fila. En modo RTL, el método busca espacio a la izquierda de la última celda agregada.

Si conoce la posición exacta de la nueva celda, utilice la otra sobrecarga del método Cell. El que acepta un delegado de tipo Action<TableCell>. En el código delegado, especifique la fila y el índice de la celda llamando a los métodos RowIndex y ColumnIndex del objeto proporcionado.

Es posible especificar sólo un índice de fila o columna para la nueva celda. El índice de columna es 0 cuando solo especifica un índice de fila. Y viceversa.

Al crear celdas, puede mezclar llamadas a ambas sobrecargas de Cell en cualquier orden.

Usaré el siguiente método de extensión en los siguientes ejemplos de código de este artículo. Esto debería hacer que los ejemplos sean más concisos y fáciles de leer.

static class LayoutHelpers
{
    public static TextSpan BoxWithText(this LayoutContainer cell, string caption)
        => cell.Border(b => b.Thickness(0.5))
            .Background(new PdfGrayColor(75))
            .ShowOnce()
            .MinWidth(50)
            .MinHeight(50)
            .Padding(10)
            .AlignCenter()
            .AlignMiddle()
            .Text(caption);
}

A continuación se muestra un ejemplo que crea una tabla con celdas colocadas tanto explícita como implícitamente. Utilicé el método de extensión para aplicar estilo y texto a las celdas. Los números en las celdas reflejan el orden de creación de las celdas.

PdfDocumentBuilder.Create().Generate("table-cells.pdf", doc =>
{
    doc.Pages(page =>
    {
        page.Size(300, 200);
        page.Content()
            .Padding(10)
            .MinimalBox()
            .Border(b => b.Thickness(0.1))
            .Table(table =>
            {
                table.Columns(columns =>
                {
                    for (int i = 0; i < 4; i++)
                        columns.RelativeColumn();
                });

                table.Cell().BoxWithText("1");
                table.Cell(c => c.RowIndex(1).ColumnIndex(1)).BoxWithText("2");
                table.Cell().BoxWithText("3");
                table.Cell(c => c.RowIndex(2).ColumnIndex(2)).BoxWithText("4");
                table.Cell(c => c.RowIndex(0).ColumnIndex(3)).BoxWithText("5");
            });
    });
});

Puede ver el resultado del código en table-cells.pdf.

Tramos de filas y columnas

Las celdas pueden abarcar varias filas y/o varias columnas. Utilice la sobrecarga Cell que acepta un delegado para especificar el número de filas y columnas que abarca la celda.

PdfDocumentBuilder.Create().Generate("table-cellspans.pdf", doc =>
{
    doc.Pages(page =>
    {
        page.Size(300, 150);
        page.Content()
            .Padding(10)
            .MinimalBox()
            .Border(b => b.Thickness(0.1))
            .Table(table =>
            {
                table.Columns(columns =>
                {
                    for (int i = 0; i < 4; i++)
                        columns.RelativeColumn();
                });

                table.Cell(c => c.RowSpan(2).ColumnSpan(2)).BoxWithText("1");
                table.Cell(c => c.ColumnSpan(2)).BoxWithText("2");
                table.Cell().BoxWithText("3");
                table.Cell().BoxWithText("4");
            });
    });
});

Puede ver el resultado del código en table-cellspans.pdf.

Superpuestos

Es posible que las células se cubran entre sí. Cuando esto sucede, el orden de creación define qué celda se pondrá encima de otra.

Puede obtener algunas celdas superpuestas cuando sus celdas abarcan más de una columna o fila. O cuando posicionas celdas tanto explícita como implícitamente.

PdfDocumentBuilder.Create().Generate("table-overlapping.pdf", doc =>
{
    doc.Pages(page =>
    {
        page.Size(300, 300);
        page.Content()
            .Padding(10)
            .MinimalBox()
            .Border(b => b.Thickness(0.1))
            .Table(table =>
            {
                table.Columns(columns =>
                {
                    for (int i = 0; i < 4; i++)
                        columns.RelativeColumn();
                });

                for (int i = 0; i < 16; i++)
                    table.Cell().BoxWithText($"{i + 1}");

                table.Cell(c => c.RowIndex(2).ColumnIndex(1).ColumnSpan(3))
                    .Background(new PdfRgbColor(250, 123, 5), 50);
            });
    });
});

Puede ver el resultado del código en table-overlapping.pdf.

Extender hasta el fondo

Si el diseño de su tabla es complejo, el mecanismo de paginación puede generar un espacio en blanco en la parte inferior de algunas de sus columnas. Utilice el método ExtendCellsToBottom si desea deshacerse del espacio en blanco. El método extiende la última celda de cada columna para que las celdas terminen en la parte inferior de la tabla.

En el siguiente ejemplo, la primera página muestra el comportamiento predeterminado. La segunda página muestra cómo la llamada ExtendCellsToBottom afecta las columnas.

PdfDocumentBuilder.Create().Generate("table-extendcells.pdf", doc =>
{
    for (int take = 0; take < 2; take++)
    {
        doc.Pages(page =>
        {
            page.Size(300, 200);
            page.Content()
                .Padding(10)
                .MinimalBox()
                .Border(b => b.Thickness(0.1))
                .Table(table =>
                {
                    if (take != 0)
                        table.ExtendCellsToBottom();

                    table.Columns(columns =>
                    {
                        for (int i = 0; i < 4; i++)
                            columns.RelativeColumn();
                    });

                    table.Cell(c => c.RowIndex(0).ColumnIndex(0)).BoxWithText("1");
                    table.Cell(c => c.RowIndex(2).ColumnIndex(0)).BoxWithText("2");
                    table.Cell(c => c.RowIndex(1).ColumnIndex(1)).BoxWithText("3");
                    table.Cell(c => c.RowIndex(2).ColumnIndex(2)).BoxWithText("4");
                    table.Cell(c => c.RowIndex(1).ColumnIndex(3)).BoxWithText("5");
                });
        });
    }
});

Puede ver el resultado del código en table-extendcells.pdf.

Las tablas pueden tener un encabezado y un pie de página. Utilice los métodos Header y Footer para acceder y configurar el TableCellContainer correspondiente. El contenedor proporciona los métodos Cell para crear celdas. Esos métodos funcionan exactamente igual que los métodos para el contenido de la tabla principal.

El encabezado, el pie de página y las celdas de la tabla principal utilizan la misma definición de columna. Pero las tres colecciones de células son independientes y no se afectan entre sí.

Cuando la tabla no cabe en una página, el encabezado y el pie de página se repiten en cada página ocupada por la tabla.

Utilicé el método de extensión BoxWithText en los ejemplos anteriores. También utilizo el método de extensión WhiteBoxWithText para el siguiente código.

static class LayoutHelpers
{
    public static TextSpan WhiteBoxWithText(
        this LayoutContainer cell, string caption, bool center = true)
    {
        return cell.Border(b => b.Thickness(0.5))
            .ShowOnce()
            .MinWidth(50)
            .MinHeight(50)
            .Padding(10)
            .Container(l => center ? l.AlignCenter() : l)
            .AlignMiddle()
            .Text(caption);
    }
}

El siguiente ejemplo muestra cómo crear una tabla con un encabezado. Utilizo una tabla con cinco elementos químicos esenciales. Existe el nombre y el punto de fusión en grados Celsius y Kelvin para cada elemento.

var elements = new (string Name, double Celsius, double Kelvin)[] {
    ("Oxygen", -218.79, 54.36),
    ("Carbon", double.NaN, double.NaN),
    ("Hydrogen", -259.16, 13.99),
    ("Nitrogen", -209.86, 63.23),
    ("Sulfur", 115.21, 388.36),
};

static string formatDouble(double val)
{
    return double.IsNaN(val)
        ? string.Empty
        : val.ToString(CultureInfo.InvariantCulture);
}

PdfDocumentBuilder.Create().Generate("table-header.pdf", doc =>
{
    doc.Pages(page =>
    {
        page.Size(400, 500);
        page.Content()
            .Padding(10)
            .MinimalBox()
            .Border(b => b.Thickness(0.1))
            .Table(table =>
            {
                table.Columns(columns =>
                {
                    columns.RelativeColumn();
                    columns.ConstantColumn(100);
                    columns.ConstantColumn(100);
                });

                table.Header(header =>
                {
                    header.Cell(c => c.RowSpan(2)).BoxWithText("Chemical element");
                    header.Cell(c => c.ColumnSpan(2)).BoxWithText("Melting point");

                    header.Cell().BoxWithText("Celsius");
                    header.Cell().BoxWithText("Kelvin");
                });

                foreach (var (Name, Celsius, Kelvin) in elements)
                {
                    table.Cell().WhiteBoxWithText(Name, false);

                    table.Cell().WhiteBoxWithText(formatDouble(Celsius));
                    table.Cell().WhiteBoxWithText(formatDouble(Kelvin));
                }
            });
    });
});

Puede ver el resultado del código en table-header.pdf.

Código de muestra

Dedique algo de tiempo a comprobar el ejemplo Agregar tablas a documentos PDF. También muestra cómo crear tablas con encabezados, pero no utiliza ningún método de extensión. Y también existe una versión VB.NET.