Код
Получив закладку, вы можете получить доступ к ее родительскому элементу и добавить другие элементы после нее.
using (WordprocessingDocument document = WordprocessingDocument.Open(@"C:\Path\filename.docx", true))
{
var mainPart = document.MainDocumentPart;
var res = from bm in mainPart.Document.Body.Descendants<BookmarkStart>()
where bm.Name == "BookmarkName"
select bm;
var bookmark = res.SingleOrDefault();
if (bookmark != null)
{
var parent = bookmark.Parent; // bookmark's parent element
// simple paragraph in one declaration
//Paragraph newParagraph = new Paragraph(new Run(new Text("Hello, World!")));
// build paragraph piece by piece
Text text = new Text("Hello, World!");
Run run = new Run(new RunProperties(new Bold()));
run.Append(text);
Paragraph newParagraph = new Paragraph(run);
// insert after bookmark parent
parent.InsertAfterSelf(newParagraph);
var table = new Table(
new TableProperties(
new TableStyle() { Val = "TableGrid" },
new TableWidth() { Width = 0, Type = TableWidthUnitValues.Auto }
),
new TableGrid(
new GridColumn() { Width = (UInt32Value)1018U },
new GridColumn() { Width = (UInt32Value)3544U }),
new TableRow(
new TableCell(
new TableCellProperties(
new TableCellWidth() { Width = 0, Type = TableWidthUnitValues.Auto }),
new Paragraph(
new Run(
new Text("Category Name"))
)),
new TableCell(
new TableCellProperties(
new TableCellWidth() { Width = 4788, Type = TableWidthUnitValues.Dxa }),
new Paragraph(
new Run(
new Text("Value"))
))
),
new TableRow(
new TableCell(
new TableCellProperties(
new TableCellWidth() { Width = 0, Type = TableWidthUnitValues.Auto }),
new Paragraph(
new Run(
new Text("C1"))
)),
new TableCell(
new TableCellProperties(
new TableCellWidth() { Width = 0, Type = TableWidthUnitValues.Auto }),
new Paragraph(
new Run(
new Text("V1"))
))
));
// insert after new paragraph
newParagraph.InsertAfterSelf(table);
}
// close saves all parts and closes the document
document.Close();
}
Код выше должен это сделать. Однако я объясню некоторые особые обстоятельства.
Имейте в виду, что он попытается вставить после родительского элемента закладки. Какое поведение вы ожидаете, если ваша закладка окажется частью абзаца внутри таблицы? Должен ли он добавить новый параграф и таблицу сразу после него, внутри этой таблицы? Или он должен делать это после этой таблицы?
Вам может быть интересно, почему вышеупомянутые вопросы имеют значение. Все зависит от того, где будет происходить вставка. Если родительский элемент закладки находится в таблице, в настоящий момент приведенный выше код будет пытаться поместить таблицу в таблицу. Это нормально, однако может произойти ошибка из-за неверной структуры OpenXml. Причина в том, что если вставленная таблица была последним элементом в TableCell исходной таблицы, то после закрывающего тега TableCell должен быть добавлен элемент Paragraph. Вы сразу обнаружите эту проблему, если она возникла после попытки открыть документ в MS Word.
Решение состоит в том, чтобы определить, действительно ли вы выполняете вставку в таблицу.
Для этого мы можем добавить к приведенному выше коду (после родительского var):
var parent = bookmark.Parent; // bookmark's parent element
// loop till we get the containing element in case bookmark is inside a table etc.
// keep checking the element's parent and update it till we reach the Body
var tempParent = bookmark.Parent;
bool isInTable = false;
while (tempParent.Parent != mainPart.Document.Body)
{
tempParent = tempParent.Parent;
if (tempParent is Table && !isInTable)
isInTable = true;
}
// ...
newParagraph.InsertAfterSelf(table); // from above sample
// if bookmark is in a table, add a paragraph after table
if (isInTable)
table.InsertAfterSelf(new Paragraph());
Это должно предотвратить возникновение ошибки и предоставить вам действительный OpenXml. Идея цикла while может быть использована, если вы ответили «да» на мой предыдущий вопрос и хотите выполнить вставку после родительской таблицы, а не внутри таблицы, как это делал приведенный выше код. Если это так, вышеуказанная проблема больше не будет проблемой, и вы можете заменить этот цикл и логическое значение следующим:
var parent = bookmark.Parent; // bookmark's parent element
while (parent.Parent != mainPart.Document.Body)
{
parent = parent.Parent;
}
Это продолжает переопределять родительский элемент, пока он не станет основным содержащим элементом на уровне тела. Таким образом, если закладка находится в абзаце, который находится в таблице, она перейдет из Paragraph в TableCell в TableRow в Table и остановится на этом, поскольку родительским элементом таблицы является Body. В этот момент родитель = элемент таблицы, и мы можем вставить после него.
Это должно охватывать несколько разных подходов, в зависимости от вашего первоначального намерения. Дайте мне знать, если вам понадобятся какие-либо разъяснения после того, как вы попробуете.
Отражатель документа
Вам может быть интересно, как я определил значения GridColumn.Width
. Я сделал таблицу и использовал инструмент Document Reflector, чтобы получить ее. Когда вы устанавливали Open Xml SDK, инструменты повышения производительности (если вы их устанавливали) находились в C:\Program Files\Open XML Format SDK\V2.0\tools
(или аналогичном).
Лучший способ узнать, как работает формат * .docx (или любой документ в формате Open Xml), - это открыть существующий файл с помощью инструмента «Отражатель документов». Перейдите к части документа и найдите элементы, которые вы хотите скопировать. Инструмент показывает вам фактический код, использованный для генерации всего документа. Это код, который вы можете скопировать / вставить в свое приложение для получения похожих результатов. Вы можете игнорировать все ссылочные идентификаторы обычно; вам придется взглянуть и попробовать его, чтобы почувствовать это.
Как я уже говорил, приведенный выше код таблицы был взят из образца документа. Я добавил простую таблицу в docx, затем открыл ее в инструменте и скопировал код, сгенерированный инструментом (я удалил некоторые дополнительные функции, чтобы очистить его). Это дало мне рабочий образец для добавления таблицы.
Это особенно полезно, когда вы хотите знать, как написать код, который генерирует что-то, например отформатированные таблицы и абзацы со стилями и т. Д.
Посмотрите по этой ссылке скриншоты и информацию о других инструментах, включенных в SDK: Введение в Open XML SDK 2.0 .
Фрагменты кода
Возможно, вас также заинтересуют фрагменты кода для Open Xml. Для просмотра списка фрагментов проверьте это сообщение в блоге . Вы можете скачать их здесь: 2007 Образец системы Office: фрагменты кода в формате Open XML формата SDK 2.0 для Visual Studio 2008 .
После установки вы добавите их из Tools | Меню Диспетчера фрагментов кода. Выберите C # для языка, нажмите кнопку «Добавить» и перейдите к PersonalFolder \ Visual Studio 2008 \ Фрагменты кода \ Visual C # \ Откройте XML SDK 2.0 для Microsoft Office , чтобы добавить их. В своем коде вы должны щелкнуть правой кнопкой мыши и выбрать «Вставить фрагмент» и выбрать тот, который вы хотите.