该页面可以包含自动翻译的文本。

Table 容器

使用 Table 容器来布局最复杂的数据。表格在 PDF 文档中起着至 关重要的作用,可以提高文档的清晰度和有效性。

表格提供了一种结构化的格式来呈现数据。它们允许您以简洁且视觉上有吸引力的方式呈现复杂的细节。您可以 使用表格简洁地呈现事实、数据和趋势,而无需冗长的段落。

精心设计的表格可提高可读性。通过对齐列和行,您可以创建引导读者视线的结构。您可以使用背景、边框和字 体样式来强调特定的单元格或标题。

Table 容器

通过调用 LayoutContainer.Table 方法获取表格 容器。然后必须在容器中定义至少一个列。最简单的情况下,可以通过多次调用 Cell 方法来填充所有列和行。

请继续阅读以了解如何定义列、向表添加页眉和/或页脚。本文还描述了 Table 容器的所有其他功能。

本文是有关用于 PDF 生成的 Layout API 的系列文章的一部分。 如果您是 API 新手,请先阅读 Layout API 入门 部分。

Docotic.Pdf 库 9.5.17615-dev 布局附加组件 9.5.17615-dev
回归测试 14,813 通过 NuGet 总下载量 4,924,084

列和行

每个表必须定义至少一个列。使用 Table.Columns 方法定 义列。该方法接受 Action<TableColumnContainer> 类型的委托。 在委托代码中,通过调用所提供对象的方法向表中添加列。

ConstantColumn 方法添加一个宽 度等于精确点数的列。

RelativeColumn 方法添加具有相 对宽度的列。该方法接受列应占据的部分数。部分总数是此表中所有 RelativeColumn 调用的所有数字的总 和。大多数情况下,此方法的工作原理与 Row 容器RelativeItem 相同。请阅读链接以 获取详细说明。

无需明确定义行。行数取决于列和单元格属性。单元格数也会影响行数。表的高度可以大于所有行高的总和。行 的单元格数可以少于列数。列的单元格数也可以少于行数。

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

您可以在 table-basic.pdf 中看到代码的结果。

在生成的 PDF 中,表格占据了所有父容器空间。即使没有足够的单元格来覆盖该区域。取消注释 MinimalBox 调用以将表格高度限制为其行 高的总和。

代码定义了三列,并向表格添加了 10 个单元格。表格将单元格一个接一个地放置,并在需要时添加新行。一行 只有一个单元格是完全没问题的。

如果当前页面空间不足,表格会将单元格放在下一页。您可以通过增加添加的单元格数量来观察这一点。

细胞

让我解释一下 Layout API 为处理表格单元格提供了什么。

位置

有两种方法可以创建表格单元格:

  • 不带明​​确的位置信息
  • 至少部分指定位置

我在上一节的代码中使用了第一种方法。您调用不接受参数的 Cell 方法重载。该方法在最后一个添加的单元格后的列中添 加一个新单元格。如果右侧没有空间,则单元格将进入下一行。在 RTL 模式下,该方法会检查最后一个添加的单 元格左侧的空间。

如果您知道新单元格的确切位置,请使用 Cell 方法的另一个重载。该方法接受 Action<TableCell> 类型的委托。在委托代码中,通过调用 所提供对象的 RowIndexColumnIndex 方法指定单元格行和索引。

可以为新单元格仅指定行或列索引。如果仅指定行索引,则列索引为 0。反之亦然。

创建单元格时,您可以按任意顺序混合对两个 Cell 重载的调用。

我将在本文的下一个代码示例中使用以下 扩展方法。这将使示例更加简洁且更易于阅读。

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

下面是一个创建表格的示例,其中既有显式放置的单元格,也有隐式放置的单元格。我使用扩展方法将样式和文 本应用于单元格。单元格中的数字反映了单元格的创建顺序。

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

您可以在 table-cells.pdf 中看到代码的结果。

行和列的跨度

单元格可以跨越多行和/或多列。使用接受委托的 Cell 重载 来指定单元格跨越的行数和列数。

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

您可以在 table-cellspans.pdf 中看到代码的结果。

重叠

细胞之间可以相互覆盖。当这种情况发生时,产生的顺序决定了哪个细胞将位于另一个细胞之上。

当单元格跨越多列或多行时,或者当您显式或隐式地定位单元格时,可能会出现一些重叠单元格。

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

您可以在 table-overlapping.pdf 中看到代码的结 果。

延伸至底部

如果表格布局复杂,分页机制可能会导致某些列底部出现空白。如果要消除空白,请使用 ExtendCellsToBottom 方法。该方法会扩展 每列的最后一个单元格,使单元格结束于表格底部。

在以下示例中,第一页显示默认行为。第二页显示 ExtendCellsToBottom 调用如何影响列。

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

您可以在 table-extendcells.pdf 中看到代码的结 果。

表格可以有页眉和页脚。使用 HeaderFooter 方法访问和设置相应的 TableCellContainer。容器提供 Cell 方法来创建单元格。这些方法的工作方式 与主表格内容的方法完全相同。

页眉、页脚和主表单元格使用相同的列定义。但这三个单元格集合都是独立的,不会相互影响。

当表格无法容纳在一页中时,页眉和页脚会在表格占据的每一页上重复。

我在前面的例子中使用了 BoxWithText 扩展方法。我还在下一个代码中使用了 WhiteBoxWithText 扩展方 法。

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

以下示例显示如何创建带有标题的表格。我使用包含五种基本化学元素的表格。每个元素都有名称和熔点(摄氏 度和开尔文度)。

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

您可以在 table-header.pdf 中看到代码的结果。

示例代码

请花点时间查看 将表格添加到 PDF 文档 示例。它还展示了如何创建带标题的表格,但不使用任何扩展方法。 并且还有一个 VB.NET 版本。