Всем привет! Я Александр Родов, ведущий разработчик в компании «БАРС Груп», автор и руководитель разработки сервиса генерации печатных форм Sprinter. Этой статьей мы продолжаем рассказ о возможностях использования библиотек DocumentFormat.OpenXml для генерации печатных файлов «офисных» форматов.
Напомним, в предыдущей части мы сформулировали постановку нашей демонстрационной задачи, а именно разработать печать данных заказа в некотором абстрактном интернет-магазине. Далее реализовали первую часть документа, содержащую шапку документа и колонтитул с логотипом магазина. Подробнее с постановкой задачи и примерами исходного кода можно ознакомиться по ссылке выше. Ну а на очереди у нас — печать таблиц в docx!

Создание и настройка таблицы
Для создания пустой таблицы в docx заполним объекты с настройками таблицы, которые будут являться дочерними для объекта таблицы в структуре OpenXml. Объект TableProperties содержит описание границ и макета таблицы, а объект TableGrid — описание набора столбцов таблицы. Для границ зададим цвет, размер и стиль — одинарную границу. Для столбцов — в нашей таблице их будет шесть — укажем ширину в единицах TWIP, описание которых приводилось в предыдущей статье.
var tableProps = new TableProperties { TableBorders = new TableBorders { LeftBorder = new() { Color = DefaultColor, Size = 12u, Val = BorderValues.Single }, RightBorder = new() { Color = DefaultColor, Size = 12u, Val = BorderValues.Single }, TopBorder = new() { Color = DefaultColor, Size = 12u, Val = BorderValues.Single }, BottomBorder = new() { Color = DefaultColor, Size = 12u, Val = BorderValues.Single }, }, TableLayout = new TableLayout { Type = TableLayoutValues.Fixed } }; var tableGrid = new TableGrid( new GridColumn { Width = CmToTwip(1).ToString() }, new GridColumn { Width = CmToTwip(3).ToString() }, new GridColumn { Width = CmToTwip(4.5f).ToString() }, new GridColumn { Width = CmToTwip(2.5f).ToString() }, new GridColumn { Width = CmToTwip(3).ToString() }, new GridColumn { Width = CmToTwip(3).ToString() }); var wordTable = new Table(tableProps, tableGrid); document.MainDocumentPart.Document.Body.Append(wordTable);
Далее заполним титульную строку таблицу. Для этого инициализируем объект TableRow и добавим в него шесть объектов TableCell с указанием текста заголовка и настроек границ ячейки. К самому тексту в ячейке применим настройки стилей текста.
var cellBorders = new TableCellBorders { LeftBorder = new() { Color = DefaultColor, Size = 12u, Val = BorderValues.Single }, RightBorder = new() { Color = DefaultColor, Size = 12u, Val = BorderValues.Single }, TopBorder = new() { Color = DefaultColor, Size = 12u, Val = BorderValues.Single }, BottomBorder = new() { Color = DefaultColor, Size = 12u, Val = BorderValues.Single }, }; var titleRow = new TableRow(new TableRowProperties(new TableHeader())); var tableTitles = new[] { "№", "Код", "Наименование", "Количество", "Цена", "Стоимость" }; foreach (var title in tableTitles) { var cell = new TableCell { TableCellProperties = new TableCellProperties { Shading = new Shading { Fill = "DDDDDD", Val = ShadingPatternValues.Clear }, TableCellBorders = (TableCellBorders)cellBorders.CloneNode(true) } }; SetCellText(cell, title, JustificationValues.Center); titleRow.Append(cell); } wordTable.Append(titleRow); void SetCellText(TableCell cell, string text, JustificationValues justificationValues, bool? bold = null) { var paragraphProperties = new ParagraphProperties { ParagraphStyleId = new ParagraphStyleId { Val = TableStyleId }, Justification = new Justification { Val = justificationValues }, SpacingBetweenLines = new SpacingBetweenLines { After = "0", Line = "360", LineRule = LineSpacingRuleValues.Auto } }; cell.Append(new Paragraph(new Run(new Text(text) { Space = SpaceProcessingModeValues.Preserve }) { RunProperties = bold.HasValue ? new RunProperties { Bold = new Bold { Val = bold.Value } } : null }) { ParagraphProperties = paragraphProperties }); }
Заполнение ячеек
Теперь нам необходимо внести данные заказа в таблицу. Алгоритм вставки ячеек в таблицу здесь будет аналогичен тому, как это делалось для строки шапки таблицы. Разница будет лишь в параметрах стилей (жирное начертание, горизонтальное выравнивание), которые передаются в метод SetCellText.
Ещё раз стоит обратить внимание на то, что для переиспользования одних и тех же объектов OpenXml в документе их необходимо клонировать, таково требование API OpenXml. Именно поэтому для каждой ячейки параметры границ TableCellBorders инициализируются копией переменной cellBorders. Код внесения данных заказа в таблицу ниже:
var totalSum = 0m; var counter = 1; foreach (var purchasePosition in data.Items) { var row = new TableRow(new TableRowProperties()); var numberCell = new TableCell(new TableCellProperties { TableCellBorders = (TableCellBorders)cellBorders.CloneNode(true) }); SetCellText(numberCell, counter++.ToString(), JustificationValues.Center); row.Append(numberCell); var codeCell = new TableCell(new TableCellProperties { TableCellBorders = (TableCellBorders)cellBorders.CloneNode(true) }); SetCellText(codeCell, purchasePosition.ProductCode, JustificationValues.Center); row.Append(codeCell); var nameCell = new TableCell(new TableCellProperties { TableCellBorders = (TableCellBorders)cellBorders.CloneNode(true) }); SetCellText(nameCell, purchasePosition.ProductName, JustificationValues.Left); row.Append(nameCell); var countCell = new TableCell(new TableCellProperties { TableCellBorders = (TableCellBorders)cellBorders.CloneNode(true) }); SetCellText(countCell, purchasePosition.Count.ToString(), JustificationValues.Center); row.Append(countCell); var priceCell = new TableCell(new TableCellProperties { TableCellBorders = (TableCellBorders)cellBorders.CloneNode(true) }); SetCellText(priceCell, $"{purchasePosition.UnitPrice} руб.", JustificationValues.Center); row.Append(priceCell); var sum = purchasePosition.Count * purchasePosition.UnitPrice; totalSum += sum; var sumCell = new TableCell(new TableCellProperties { TableCellBorders = (TableCellBorders)cellBorders.CloneNode(true) }); SetCellText(sumCell, $"{sum} руб.", JustificationValues.Center); row.Append(sumCell); wordTable.Append(row); }
Добавление строки итогов
Для вывода суммы заказа добавим в таблицу еще одну строку и добавим ей объединение ячеек по столбцам, как показано ниже:
var totalCell = new TableCell(new TableCellProperties { TableCellBorders = (TableCellBorders)cellBorders.CloneNode(true), GridSpan = new GridSpan { Val = 6 } }); SetCellText(totalCell, $"Общая сумма заказа: {totalSum} руб.", JustificationValues.Right, bold: true); var totalRow = new TableRow(new TableRowProperties(), totalCell); wordTable.Append(totalRow);
Горизонтальное объединение ячеек задается с помощью свойства TableCellProperties.GridSpan с указанием количества столбцов, которые входят в объединение. В нашем случае это все шесть столбцов таблицы.
Если бы нам требовалось вертикальное объединение ячеек, для этого мы использовали бы свойство TableCellProperties.VerticalMerge. Однако, в отличие от GridSpan, в VerticalMerge уже не задается количество строк, входящих в объединение. В вертикальное объединение попадут все строки, находящиеся под выбранной ячейкой, вплоть до новой ячейки, в которой будет задано свойство VerticalMerge со значением Val=MergedCellValues.Restart.
Заключение
В этой статье мы добавили таблицу в наш печатный документ заказа в интернет-магазине. Готовый документ выглядит так:

Для реалистичности примера сюда можно также добавить прочие элементы, такие как контактные данные магазина, место для подписи клиента о получении заказа, и т.д. Здесь же мы не будем приводить вставку таких элементов, так как все наши действия и приемы в этом случае будут аналогичны уже изложенным в этой и предыдущей статьях. Например, для вставки шаблона подписи клиента можно использовать таблицу, в которой скрыты границы и задано выравнивание по правому краю листа.
На этом наш демонстрационный docx-документ завершен, а вместе с ним и обзор основных возможностей печати docx с помощью DocumentFormat.OpenXml. Безусловно, здесь демонстрируется небольшая часть возможностей, доступных в спецификации OpenXML. Формат поддерживает как целые разделы элементов, не вошедшие в наш обзор (например, рисованные фигуры и диаграммы), так и разнообразные настройки упомянутых элементов: текста, таблиц, изображений. Однако в наших статьях мы постарались охватить наиболее популярное содержимое типичного документа — текст, таблицы и изображения, а также основные приемы работы с ними в .NET.
С исходным кодом всего проекта можно ознакомиться по ссылке. А в заключительной части нашего цикла статей об использовании DocumentFormat.OpenXml мы рассмотрим еще один распространённый Use-case – печать таблиц в xlsx.
Мы в «БАРС Груп» разрабатываем цифровые решения для государства, бизнеса и людей. Принимаем активное участие в реализации Национального проекта «Цифровая экономика» и создаем цифровые решения для импортозамещения программного обеспечения – 88 решений компании зарегистрировано в реестре российского ПО. Рассказываем о наших продуктах и ИТ-трендах в Telegram-канале. Сервис печати Sprinter также входит в реестр ПО. Он помогает разработчикам и аналитикам с печатью документов по заданным шаблонам и благодаря ему увидела свет эта статья!
ссылка на оригинал статьи https://habr.com/ru/articles/900920/
Добавить комментарий