Questa pagina può contenere testo tradotto automaticamente.

Contenitore Table

Utilizza i contenitori Table per disporre i dati più complessi. Le tabelle svolgono un ruolo cruciale nei documenti PDF, migliorandone la chiarezza e l'efficacia.

Le tabelle forniscono un formato strutturato per presentare i dati. Ti consentono di presentare dettagli complessi in modo conciso e visivamente accattivante. Invece di lunghi paragrafi, puoi utilizzare tabelle per presentare fatti, cifre e tendenze in modo conciso.

Una tabella ben progettata migliora la leggibilità. Allineando colonne e righe, crei una struttura che guida l'occhio del lettore. Puoi utilizzare sfondi, bordi e stili di carattere per enfatizzare celle o intestazioni specifiche.

Contenitore Table

Ottieni un contenitore di tabella chiamando il metodo LayoutContainer.Table. Quindi è necessario 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 sapere 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.

Questo articolo fa parte di una serie sull'API Layout per la generazione di PDF. Se sei nuovo all'API, leggi prima la parte Introduzione all'API Layout.

Libreria Docotic.Pdf 9.4.17467-dev Componente aggiuntivo di layout 9.4.17467-dev
Test di regressione Ne sono passati 14,760 Download totali di NuGet 4,415,970

Colonne e righe

Ogni tabella deve definire almeno una colonna. Utilizza il metodo Table.Columns per definire le colonne. Il metodo accetta un delegato di tipo Action<TableColumnContainer>. Nel codice delegato aggiungere 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 una larghezza relativa. Il metodo accetta il numero di parti che la colonna dovrebbe 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 allo stesso modo di RelativeItem del contenitore Row. Si prega di leggere il collegamento per la spiegazione dettagliata.

Non è necessario definire esplicitamente le righe. Il numero di righe dipende dalle proprietà della colonna e della cella. Il numero di celle può anche influenzare il 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 rispetto al 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 principale. Anche se non ci sono abbastanza cellule per coprire la zona. Decommenta la 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 mette le celle una dopo l'altra, aggiungendo una nuova riga quando necessario. È perfettamente corretto avere una riga con una sola cella.

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

Celle

Lasciami spiegare cosa fornisce l'API Layout per lavorare con le celle della tabella.

Posizione

Esistono due modi per creare una cella di tabella:

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

Ho usato il primo modo 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 va alla riga successiva. In modalità RTL, il metodo controlla lo spazio a sinistra dell'ultima cella aggiunta.

Se conosci la posizione esatta della nuova cella, utilizza l'altro sovraccarico del metodo Cell. Quello che accetta un delegato di tipo Action<TableCell>. Nel codice 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 della colonna è 0 quando si specifica solo un indice della riga. E viceversa.

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

Utilizzerò il seguente metodo di estensione nei prossimi esempi di codice in questo articolo. Ciò 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 che implicitamente. Ho utilizzato 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.

Si estende su righe e colonne

Le celle possono estendersi su più righe e/o più colonne. Utilizza l'overload Cell che accetta un delegato per specificare il numero di righe e colonne coperte 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 cellule si coprano a vicenda. Quando ciò accade, l'ordine di creazione definisce quale cella si sovrapporrà all'altra.

Puoi ottenere alcune celle sovrapposte quando le celle si estendono su più di una colonna o riga. O quando posizioni le celle sia esplicitamente che 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 in fondo

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

Nell'esempio seguente, la prima pagina mostra il comportamento predefinito. La seconda pagina mostra come la chiamata ExtendCellsToBottom influisce sulle 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. Utilizza 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 i metodi per il contenuto della tabella principale.

Le celle dell'intestazione, del piè di pagina e della tabella principale utilizzano la stessa definizione di colonna. Ma tutte e tre le raccolte di cellule sono indipendenti e non si influenzano a vicenda.

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

Ho utilizzato il metodo di estensione BoxWithText negli esempi precedenti. Utilizzo anche il metodo di estensione WhiteBoxWithText per il codice successivo.

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. Utilizzo una tabella con cinque elementi chimici essenziali. Per ogni elemento c'è il nome e il punto di fusione sia in gradi Celsius che 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

Ti invitiamo a dedicare un po' di tempo a controllare l'esempio Aggiungi tabelle ai documenti PDF. Mostra anche come creare tabelle con intestazioni, ma non utilizza alcun metodo di estensione. Ed esiste anche una versione VB.NET.