Эта страница может содержать автоматически переведенный текст.
Контейнер таблицы
Используйте контейнеры Table для компоновки самых сложных данных. Таблицы играют важную роль в PDF-документах, повышая их наглядность и эффективность.
Таблицы обеспечивают структурированный формат представления данных. Они позволяют кратко и визуально привлекательно показывать сложные сведения. Вместо длинных абзацев можно лаконично представлять факты, числа и тенденции.
Хорошо спроектированная таблица повышает удобочитаемость. Выравнивая столбцы и строки, вы создаете структуру, которая направляет взгляд читателя. Можно использовать фоны, границы и стили шрифта, чтобы выделить отдельные ячейки или заголовки.

Получите контейнер таблицы, вызвав метод LayoutContainer.Table. Затем необходимо определить в контейнере как минимум один столбец. В простейшем случае можно заполнить все столбцы и строки, многократно вызвав метод Cell.
Продолжайте чтение, чтобы узнать, как задавать столбцы, добавлять заголовок и/или нижний колонтитул к таблице. В тексте также описаны все остальные возможности контейнера Table.
Эта статья входит в серию материалов о Layout API для генерации PDF. Если вы впервые работаете с API, сначала прочитайте раздел Начало работы с Layout API.
Столбцы и строки
Каждая таблица должна определять как минимум один столбец. Используйте метод Table.Columns для определения столбцов. Метод принимает делегат типа Action<TableColumnContainer>. В коде делегата добавляйте столбцы в таблицу, вызывая методы предоставленного объекта.
Метод ConstantColumn добавляет столбец с шириной, равной точному числу пунктов.
Метод RelativeColumn добавляет столбец с относительной шириной. Метод принимает число частей, которое должен занимать столбец. Общее число частей — это сумма всех чисел во всех вызовах RelativeColumn в этой таблице. В основном этот метод работает так же, как RelativeItem контейнера Row container. Пожалуйста, прочитайте по ссылке подробное объяснение.
Нет необходимости задавать строки явно. Количество строк зависит от свойств столбцов и ячеек. На количество строк также может влиять число ячеек. Высота таблицы может быть больше суммы высот всех ее строк. В строках может быть меньше ячеек, чем столбцов. И столбцы могут содержать меньше ячеек, чем число строк.
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 в любом порядке.
В следующих примерах кода в этой статье я буду использовать следующий расширенный метод extension method. Это сделает примеры более лаконичными и удобными для чтения.
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.