該頁面可以包含自動翻譯的文字。
表格容器
使用 表格 容器來排版你最複雜的資料。表格在 PDF 文件中扮演關鍵角色,可提升其清晰度與效果。
表格提供結構化格式來呈現資料。它讓你能以簡潔且具視覺吸引力的方式呈現複雜細節。你可以用表格取代冗長段落,以簡明方式呈現事實、數字與趨勢。
設計良好的表格可提升可讀性。透過對齊欄與列,你建立出引導讀者視線的結構。你可以使用背景、邊框與字型樣式來強調特定儲存格或標題。

透過呼叫 LayoutContainer.Table 方法取得表格容器。接著你必須在容器中定義至少一個欄。在最簡單的情況下,你可以多次呼叫 Cell 方法來填滿所有欄與列。
請繼續閱讀,了解如何定義欄,以及如何為表格新增標頭和/或頁尾。本文也會說明 Table 容器的其他所有功能。
本文是 Layout API 用於 PDF 產生系列的一部分。如果你是第一次接觸此 API,請先閱讀 Layout API 入門 部分。
欄與列
每個表格都必須至少定義一個欄。使用 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> 的委派的那個。在委派程式碼中,透過呼叫所提供物件的 RowIndex 與 ColumnIndex 方法指定儲存格列與索引。
新儲存格可以只指定列索引或欄索引。只指定列索引時,欄索引為 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 看到程式碼的結果。
標頭與頁尾
表格可以有標頭與頁尾。使用 Header 和 Footer 方法存取並設定對應的 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 版本。