Questa pagina può contenere testo tradotto automaticamente.

Contenitore tabella

Usa i contenitori Table per impaginare i dati più complessi. Le tabelle svolgono un ruolo cruciale nei documenti PDF, migliorandone chiarezza ed efficacia.

Le tabelle forniscono un formato strutturato per presentare i dati. Consentono di presentare dettagli complessi in modo conciso e visivamente gradevole. Invece di paragrafi lunghi, puoi usare le tabelle per presentare fatti, cifre e tendenze in modo sintetico.

Una tabella ben progettata migliora la leggibilità. Allineando colonne e righe, crei una struttura che guida lo sguardo del lettore. Puoi usare sfondi, bordi e stili dei font per enfatizzare celle o intestazioni specifiche.

Contenitore tabella

Ottieni un contenitore tabella chiamando il metodo LayoutContainer.Table. Quindi devi definire almeno una colonna nel contenitore. Nel caso più semplice, puoi riempire tutte le colonne e le righe chiamando più volte il metodo Cell.

Continua a leggere per scoprire come definire le colonne e aggiungere un'intestazione e/o un piè di pagina a una tabella. Il testo descrive anche tutte le altre funzionalità del contenitore Table, too.

Questo articolo fa parte di una serie su Layout API per la generazione di PDF. Se non conosci ancora l'API, leggi prima la sezione Getting Started with Layout API.

Colonne e righe

Ogni tabella deve definire almeno una colonna. Usa il metodo Table.Columns per definire le colonne. Il metodo accetta un delegato di tipo Action<TableColumnContainer>. Nel codice del delegato, aggiungi colonne alla tabella chiamando i metodi dell'oggetto fornito.

Il metodo ConstantColumn aggiunge una colonna con larghezza pari a un numero esatto di punti.

Il metodo RelativeColumn aggiunge una colonna con larghezza relativa. Il metodo accetta il numero di parti che la colonna deve occupare. Il numero totale di parti è la somma di tutti i numeri in tutte le chiamate RelativeColumn in questa tabella. Per lo più, questo metodo funziona come RelativeItem del contenitore riga. Leggi il link per una spiegazione dettagliata.

Non è necessario definire esplicitamente le righe. Il numero di righe dipende dalle proprietà di colonne e celle. Anche il numero di celle può influire sul numero di righe. La tabella può avere un'altezza maggiore della somma delle altezze di tutte le righe. Le righe possono avere meno celle del numero di colonne. Ed è possibile che le colonne abbiano meno celle del numero di righe.

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}");
                }
            });
    });
});

Puoi vedere il risultato del codice in table-basic.pdf.

Nel PDF risultante, la tabella occupa tutto lo spazio del contenitore padre. Anche se non ci sono abbastanza celle per coprire l'area. Rimuovi il commento alla chiamata MinimalBox per limitare l'altezza della tabella alla somma delle altezze delle sue righe.

Il codice definisce tre colonne e aggiunge 10 celle alla tabella. La tabella dispone le celle una dopo l'altra, aggiungendo una nuova riga quando necessario. È perfettamente normale avere una riga con una sola cella.

La tabella inserirà le celle nella pagina successiva se la pagina corrente non ha spazio sufficiente. Puoi verificarlo aumentando il numero di celle aggiunte.

Celle

Lascia che ti spieghi cosa offre Layout API per lavorare con le celle di tabella.

Posizione

Ci sono due modi per creare una cella della tabella:

  • senza informazioni esplicite sulla posizione
  • con una posizione specificata almeno parzialmente

Ho usato il primo metodo nel codice della sezione precedente. Chiami l'overload del metodo Cell che non accetta parametri. Il metodo aggiunge una nuova cella alla colonna dopo l'ultima cella aggiunta. Se non c'è spazio a destra, la cella passa alla riga successiva. In modalità RTL, il metodo verifica lo spazio a sinistra dell'ultima cella aggiunta.

Se conosci la posizione esatta della nuova cella, usa l'altro overload del metodo Cell. Quello che accetta un delegato di tipo Action<TableCell>. Nel codice del delegato, specifica la riga e l'indice della cella chiamando i metodi RowIndex e ColumnIndex dell'oggetto fornito.

È possibile specificare solo un indice di riga o di colonna per la nuova cella. L'indice di colonna è 0 quando specifichi solo un indice di riga. E viceversa.

Quando crei le celle, puoi combinare le chiamate a entrambi gli overload di Cell in qualsiasi ordine.

Userò il seguente metodo di estensione nei prossimi esempi di codice in questo articolo. Questo dovrebbe rendere gli esempi più concisi e più facili da leggere.

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);
}

Ecco un esempio che crea una tabella con celle posizionate sia esplicitamente sia implicitamente. Ho usato il metodo di estensione per applicare stile e testo alle celle. I numeri nelle celle riflettono l'ordine di creazione delle celle.

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");
            });
    });
});

Puoi vedere il risultato del codice in table-cells.pdf.

Estensione su righe e colonne

Le celle possono estendersi su più righe e/o più colonne. Usa l'overload di Cell che accetta un delegato per specificare il numero di righe e colonne occupate dalla cella.

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");
            });
    });
});

Puoi vedere il risultato del codice in table-cellspans.pdf.

Sovrapposizione

È possibile che le celle si sovrappongano. Quando accade, l'ordine di creazione definisce quale cella verrà visualizzata sopra l'altra.

Puoi ottenere celle sovrapposte quando le celle si estendono su più di una colonna o riga. Oppure quando posizioni le celle sia esplicitamente sia implicitamente.

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);
            });
    });
});

Puoi vedere il risultato del codice in table-overlapping.pdf.

Estendi fino al fondo

Se il layout della tabella è complesso, il meccanismo di impaginazione può causare uno spazio vuoto nella parte inferiore di alcune colonne. Usa il metodo ExtendCellsToBottom se vuoi eliminare lo spazio vuoto. Il metodo estende l'ultima cella di ogni colonna in modo che le celle terminino sul fondo della tabella.

Nell'esempio seguente, la prima pagina mostra il comportamento predefinito. La seconda pagina mostra come la chiamata ExtendCellsToBottom influenzi le colonne.

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");
                });
        });
    }
});

Puoi vedere il risultato del codice in table-extendcells.pdf.

Le tabelle possono avere un'intestazione e un piè di pagina. Usa i metodi Header e Footer per accedere e configurare il corrispondente TableCellContainer. Il contenitore fornisce i metodi Cell per creare celle. Questi metodi funzionano esattamente come quelli per il contenuto principale della tabella.

Intestazione, piè di pagina e celle principali della tabella usano la stessa definizione di colonna. Ma tutte e tre le raccolte di celle sono indipendenti e non si influenzano a vicenda.

Quando la tabella non entra in una sola pagina, l'intestazione e il piè di pagina vengono ripetuti in ogni pagina occupata dalla tabella.

Ho usato il metodo di estensione BoxWithText negli esempi precedenti. Userò anche il metodo di estensione WhiteBoxWithText nel prossimo codice.

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);
    }
}

L'esempio seguente mostra come creare una tabella con un'intestazione. Uso una tabella con cinque elementi chimici essenziali. Per ciascun elemento sono indicati il nome e il punto di fusione sia in gradi Celsius sia in gradi Kelvin.

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));
                }
            });
    });
});

Puoi vedere il risultato del codice in table-header.pdf.

Codice d'esempio

Dedica un po' di tempo a consultare l'esempio Aggiungi tabelle ai documenti PDF. Mostra anche come creare tabelle con intestazioni, ma non usa alcun metodo di estensione. Ed è disponibile anche una versione VB.NET.