QuestPDF 表格
Table
是 QuestPDF 库中可用的最复杂的布局算法之一。它能实现比 Row
和 Column
元素组合更复杂的效果,并且可以大大减少代码复杂性。不过,它的计算速度略慢。
基本用法
请分析以下示例,了解如何创建一个简单的表格实例:
- 首先定义列定义,描述每个列的宽度。
- 然后,可以在其中放置任意数量的项目。每个项目对应一个位置(
Row
和Column
)。
.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();
}
});