项目

QuestPDF 表格

Table 是 QuestPDF 库中可用的最复杂的布局算法之一。它能实现比 RowColumn 元素组合更复杂的效果,并且可以大大减少代码复杂性。不过,它的计算速度略慢。

基本用法

请分析以下示例,了解如何创建一个简单的表格实例:

  1. 首先定义列定义,描述每个列的宽度。
  2. 然后,可以在其中放置任意数量的项目。每个项目对应一个位置(RowColumn)。
.Border(1)
.Table(table =>
{
    table.ColumnsDefinition(columns =>
    {
        columns.RelativeColumn();
        columns.RelativeColumn();
        columns.RelativeColumn();
        columns.RelativeColumn();
    });

    // 使用自定义 'Element' 方法,可以重用视觉配置
    table.Cell().Row(1).Column(4).Element(Block).Text("A");
    table.Cell().Row(2).Column(2).Element(Block).Text("B");
    table.Cell().Row(3).Column(3).Element(Block).Text("C");
    table.Cell().Row(4).Column(1).Element(Block).Text("D");

    // 为了简洁,您也可以使用扩展方法,参见“扩展 DSL”部分
    static IContainer Block(IContainer container)
    {
        return container
            .Border(1)
            .Background(Colors.Grey.Lighten3)
            .ShowOnce()
            .MinWidth(50)
            .MinHeight(50)
            .AlignCenter()
            .AlignMiddle();
    }
});

自动放置单元格

您不需要为每个单元格指定位置。当算法检测到单元格没有指定位置时,它会将其放在最接近的位置。

.Table(table =>
{
    table.ColumnsDefinition(columns =>
    {
        columns.RelativeColumn();
        columns.RelativeColumn();
        columns.RelativeColumn();
        columns.RelativeColumn();
    });

    table.Cell().Row(1).Column(1).Element(Block).Text("A");
    table.Cell().Row(2).Column(2).Element(Block).Text("B");
    table.Cell().Element(Block).Text("C");
    table.Cell().Element(Block).Text("D");

    // 忽略定义默认单元格样式的 Block() 方法
});

列定义

Row 元素类似,您可以定义 固定大小和相对大小的列

重要提示: 此示例使用了 扩展 DSL 部分 中介绍的扩展方法。

container
    .Padding(10)
    .Table(table =>
    {
        table.ColumnsDefinition(columns =>
        {
            columns.ConstantColumn(50);
            columns.ConstantColumn(100);
            columns.RelativeColumn(2);
            columns.RelativeColumn(3);
        });

        table.Cell().ColumnSpan(4).LabelCell("总宽度:300px");
        table.Cell().ValueCell("50px");
        table.Cell().ValueCell("100px");
        table.Cell().ValueCell("100px");
        table.Cell().ValueCell("150px");
    });

行跨列和列跨行

单元格可以跨越多行或多列:

.Table(table =>
{
    table.ColumnsDefinition(columns =>
    {
        columns.RelativeColumn();
        columns.RelativeColumn();
        columns.RelativeColumn();
        columns.RelativeColumn();
    });

    table.Cell().RowSpan(2).ColumnSpan(2).Element(Block).Text("1");
    table.Cell().ColumnSpan(2).Element(Block).Text("2");
    table.Cell().Element(Block).Text("3");
    table.Cell().Element(Block).Text("4");
    table.Cell().RowSpan(2).Element(Block).Text("5");
    table.Cell().ColumnSpan(2).Element(Block).Text("6");
    table.Cell().RowSpan(2).Element(Block).Text("7");
    table.Cell().Element(Block).Text("8");
    table.Cell().Element(Block).Text("9");

    // 忽略定义默认单元格样式的 Block() 方法
});

重叠单元格

当您手动为单元格指定位置时,单元格可以重叠:

.Table(table =>
{
    table.ColumnsDefinition(columns =>
    {
        columns.RelativeColumn();
        columns.RelativeColumn();
        columns.RelativeColumn();
    });

    table.Cell().Row(1).RowSpan(3).Column(1).ColumnSpan(3).Background(Colors.Grey.Lighten3).MinHeight(150);
    table.Cell().Row(1).RowSpan(2).Column(1).ColumnSpan(2).Background(Colors.Grey.Lighten1).MinHeight(100);
    table.Cell().Row(3).Column(3).Background(Colors.Grey.Darken1).MinHeight(50);
});

扩展最后一列到底部

当创建可能跨页的复杂表格结构时,此功能非常有用。它对每一列中的最后一个单元格应用特殊规则,使其扩展到表格底部。这可能改善表格的外观。

.Table(table =>
{
    table.ColumnsDefinition(columns =>
    {
        columns.RelativeColumn();
        columns.RelativeColumn();
        columns.RelativeColumn();
        columns.RelativeColumn();
    });

    table.ExtendLastCellsToTableBottom();

    table.Cell().Row(1).Column(1).Element(Block).Text("A");
    table.Cell().Row(3).Column(1).Element(Block).Text("B");
    table.Cell().Row(2).Column(2).Element(Block).Text("C");
    table.Cell().Row(3).Column(3).Element(Block).Text("D");
    table.Cell().Row(2).RowSpan(2).Column(4).Element(Block).Text("E");

    // 忽略定义默认单元格样式的 Block() 方法
});

请注意,块 "C" 与 "B" 和 "D" 块一起结束:

扩展最后一列到底部

当创建可能跨页的复杂表格结构时,此功能非常有用。它对每一列中的最后一个单元格应用特殊规则,使其扩展到表格底部。这可能改善表格的外观。

.Table(table =>
{
    table.ColumnsDefinition(columns =>
    {
        columns.RelativeColumn();
        columns.RelativeColumn();
        columns.RelativeColumn();
        columns.RelativeColumn();
    });

    table.ExtendLastCellsToTableBottom();

    table.Cell().Row(1).Column(1).Element(Block).Text("A");
    table.Cell().Row(3).Column(1).Element(Block).Text("B");
    table.Cell().Row(2).Column(2).Element(Block).Text("C");
    table.Cell().Row(3).Column(3).Element(Block).Text("D");
    table.Cell().Row(2).RowSpan(2).Column(4).Element(Block).Text("E");

    // 忽略定义默认单元格样式的 Block() 方法
});

请注意,块 "C" 与 "B" 和 "D" 块一起结束:

报告示例

请查看以下示例,了解如何设计报告类文档结构。

重要提示: 此示例使用了 扩展 DSL 部分 中介绍的扩展方法。

.MinimalBox()
.Border(1)
.Table(table =>
{
    table.ColumnsDefinition(columns =>
    {
        columns.ConstantColumn(100);
        columns.RelativeColumn();
        columns.ConstantColumn(100);
        columns.RelativeColumn();
    });

    table.ExtendLastCellsToTableBottom();

    table.Cell().RowSpan(3).LabelCell("项目");
    table.Cell().RowSpan(3).ShowEntire().ValueCell(Placeholders.Sentence());

    table.Cell().LabelCell("报告编号");
    table.Cell().ValueCell(i.ToString());

    table.Cell().LabelCell("日期");
    table.Cell().ValueCell(Placeholders.ShortDate());

    table.Cell().LabelCell("检查员");
    table.Cell().ValueCell("马尔钦·扎别克");

    table.Cell().ColumnSpan(2).LabelCell("早晨天气");
    table.Cell().ColumnSpan(2).LabelCell("傍晚天气");

    table.Cell().ValueCell("时间");
    table.Cell().ValueCell("7:13");

    table.Cell().ValueCell("时间");
    table.Cell().ValueCell("18:25");

    table.Cell().ValueCell("描述");
    table.Cell().ValueCell("晴朗");

    table.Cell().ValueCell("描述");
    table.Cell().ValueCell("有风");

    table.Cell().ValueCell("风");
    table.Cell().ValueCell("温和");

    table.Cell().ValueCell("风");
    table.Cell().ValueCell("强");

    table.Cell().ValueCell("温度");
    table.Cell().ValueCell("17°C");

    table.Cell().ValueCell("温度");
    table.Cell().ValueCell("32°C");

    table.Cell().LabelCell("备注");
    table.Cell().ColumnSpan(3).ValueCell(Placeholders.Paragraph());
});

表头/表尾

还可以定义表头和表尾。如果您的表格包含更多内容并跨多页,表头和表尾元素会在每一页上重复。

请注意,表头和表尾部分有自己的行集合——它们不计入内容部分。

var pageSizes = new List<(string name, double width, double height)>()
{
    ("Letter (ANSI A)", 8.5f, 11),
    ("Legal", 8.5f, 14),
    ("Ledger (ANSI B)", 11, 17),
    ("Tabloid (ANSI B)", 17, 11),
    ("ANSI C", 22, 17),
    ("ANSI D", 34, 22),
    ("ANSI E", 44, 34)
};

const int inchesToPoints = 72;

container
.Padding(10)
.MinimalBox()
.Border(1)
.Table(table =>
{
    IContainer DefaultCellStyle(IContainer container, string backgroundColor)
    {
        return container
            .Border(1)
            .BorderColor(Colors.Grey.Lighten1)
            .Background(backgroundColor)
            .PaddingVertical(5)
            .PaddingHorizontal(10)
            .AlignCenter()
            .AlignMiddle();
    }

    table.ColumnsDefinition(columns =>
    {
        columns.RelativeColumn();

        columns.ConstantColumn(75);
        columns.ConstantColumn(75);

        columns.ConstantColumn(75);
        columns.ConstantColumn(75);
    });

    table.Header(header =>
    {
        // please be sure to call the 'header' handler!

        header.Cell().RowSpan(2).Element(CellStyle).ExtendHorizontal().AlignLeft().Text("Document type");

        header.Cell().ColumnSpan(2).Element(CellStyle).Text("Inches");
        header.Cell().ColumnSpan(2).Element(CellStyle).Text("Points");

        header.Cell().Element(CellStyle).Text("Width");
        header.Cell().Element(CellStyle).Text("Height");

        header.Cell().Element(CellStyle).Text("Width");
        header.Cell().Element(CellStyle).Text("Height");

        // you can extend existing styles by creating additional methods
        IContainer CellStyle(IContainer container) => DefaultCellStyle(container, Colors.Grey.Lighten3);
    });

    foreach (var page in pageSizes)
    {
        table.Cell().Element(CellStyle).ExtendHorizontal().AlignLeft().Text(page.name);

        // inches
        table.Cell().Element(CellStyle).Text(page.width);
        table.Cell().Element(CellStyle).Text(page.height);

        // points
        table.Cell().Element(CellStyle).Text(page.width * inchesToPoints);
        table.Cell().Element(CellStyle).Text(page.height * inchesToPoints);

        IContainer CellStyle(IContainer container) => DefaultCellStyle(container, Colors.White).ShowOnce();
    }
});

Page 1:

Page 2:

在本文档中