Вставьте комментарий в текстовый документ
В этом разделе показано, как использовать классы в пакете SDK Open XML для Office для программного добавления комментария к первому абзацу в текстовом документе.
Открытие существующего документа для изменения
Чтобы открыть существующий документ, создайте WordprocessingDocument экземпляр класса, как показано в следующей using
инструкции. В той же инструкции откройте текстовый файл по указанному пути к файлу с помощью Open(String, Boolean) метода с логическим параметром , чтобы true
включить редактирование в документе.
using (WordprocessingDocument document = WordprocessingDocument.Open(fileName, true))
В версии 3.0.0+ Close() метод был удален в пользу использования инструкции using.
Это гарантирует, что Dispose() метод автоматически вызывается при достижении закрывающей фигурной скобки. Блок, следующий за инструкцией using, создает область для объекта, создаваемого или именуемого в инструкции using.
WordprocessingDocument Так как класс в пакете SDK open XML автоматически сохраняет и закрывает объект в рамках его IDisposable реализации, а так как Dispose() вызывается автоматически при выходе из блока, не нужно явно вызывать Save() или Dispose() до тех пор, пока вы используете инструкцию using
.
Механизм работы примера кода
После открытия документа необходимо найти первый абзац в документе для добавления комментария. Код находит первый абзац путем вызова First метода расширения для всех элементов-потомков элемента документа, которые имеют тип Paragraph. Метод First
является членом Enumerable класса . Класс System.Linq.Enumerable
предоставляет методы расширения для объектов, реализующих IEnumerable<T> интерфейс .
Paragraph firstParagraph = document.MainDocumentPart.Document.Descendants<Paragraph>().First();
wordprocessingCommentsPart.Comments ??= new Comments();
string id = "0";
Сначала код определяет, существует ли WordprocessingCommentsPart часть. Для этого вызовите универсальный MainDocumentPart метод GetPartsCountOfType
и укажите тип WordprocessingCommentsPart
.
Если часть WordprocessingCommentsPart
существует, код получает новое Id
значение для Comment объекта, который он добавит к существующему WordprocessingCommentsPart
Comments объекту коллекции. Для этого в объекте коллекции обнаруживается наибольшее Id
значение атрибута Comment
, заданное объекту Comments
коллекции, увеличивается значение на единицу, а затем сохраняется в качестве Id
значения. Если часть не WordprocessingCommentsPart
существует, код создает ее с помощью AddNewPart метода MainDocumentPart объекта , а затем добавляет Comments
в него объект коллекции.
if (document.MainDocumentPart.GetPartsOfType<WordprocessingCommentsPart>().Count() > 0)
{
if (wordprocessingCommentsPart.Comments.HasChildren)
{
// Obtain an unused ID.
id = (wordprocessingCommentsPart.Comments.Descendants<Comment>().Select(e =>
{
if (e.Id is not null && e.Id.Value is not null)
{
return int.Parse(e.Id.Value);
}
else
{
throw new ArgumentNullException("Comment id and/or value are null.");
}
})
.Max() + 1).ToString();
}
}
Объекты Comment
и Comments
представляют элементы comment и comments соответственно в схеме Open XML Wordprocessing. Необходимо Comment
добавить в Comments
объект , чтобы код сначала создает Comments
экземпляр объекта (с помощью строковых аргументов author
, initials
и comments
, которые были переданы в AddCommentOnFirstParagraph
метод ).
Комментарий представлен следующим примером кода WordprocessingML. .
<w:comment w:id="1" w:initials="User">
...
</w:comment>
Затем код добавляет к Comment
объекту Comments
. Это создает требуемую структуру дерева объектной модели XML-документа (DOM) в памяти, которая состоит из родительского comments
элемента с дочерними comment
элементами под ним.
Paragraph p = new Paragraph(new Run(new Text(comment)));
Comment cmt =
new Comment()
{
Id = id,
Author = author,
Initials = initials,
Date = DateTime.Now
};
cmt.AppendChild(p);
wordprocessingCommentsPart.Comments.AppendChild(cmt);
В следующем примере кода WordprocessingML показано содержимое части комментариев в документе WordprocessingML.
<w:comments>
<w:comment … >
…
</w:comment>
</w:comments>
При создании экземпляра Comment
объекта код связывает Comment
с диапазоном в документе Wordprocessing.
CommentRangeStart объекты и CommentRangeEnd соответствуют commentRangeStart
элементам и commentRangeEnd
в схеме Open XML Wordprocessing.
Объект CommentRangeStart
предоставляется в качестве аргумента методу InsertBeforeParagraph объекта , а CommentRangeEnd
объект передается в InsertAfter метод .
Таким образом создается область комментариев, которая расширяется с первого символа первого абзаца в документе Wordprocessing до последнего символа первого абзаца включительно.
Объект CommentReference представляет commentReference
элемент в схеме Open XML Wordprocessing. CommentReference связывает конкретный комментарий в WordprocessingCommentsPart
части (файл Comments.xml в пакете Wordprocessing) с определенным расположением в тексте документа ( MainDocumentPart
часть, содержащаяся в файле Document.xml в пакете Wordprocessing). Атрибут id
comment, commentRangeStart, commentRangeEnd и commentReference одинаков для данного комментария, поэтому атрибут commentReference id
должен соответствовать значению атрибута comment id
, на который он ссылается. В примере код добавляет commentReference
элемент с помощью API и создает CommentReference
экземпляр объекта, указывая Id
значение, а затем добавляет его в Run объект .
firstParagraph.InsertBefore(new CommentRangeStart()
{ Id = id }, firstParagraph.GetFirstChild<Run>());
// Insert the new CommentRangeEnd after last run of paragraph.
var cmtEnd = firstParagraph.InsertAfter(new CommentRangeEnd()
{ Id = id }, firstParagraph.Elements<Run>().Last());
// Compose a run with CommentReference and insert it.
firstParagraph.InsertAfter(new Run(new CommentReference() { Id = id }), cmtEnd);
Пример кода
В следующем примере кода показано, как создать комментарий и связать его с диапазоном в текстовом документе. Вызов метода AddCommentOnFirstParagraph
pass в пути к документу, вашем имени, инициалах и тексте комментария.
string fileName = args[0];
string author = args[1];
string initials = args[2];
string comment = args[3];
AddCommentOnFirstParagraph(fileName, author, initials, comment);
Ниже приведен полный пример кода на языках C# и Visual Basic.
// Insert a comment on the first paragraph.
static void AddCommentOnFirstParagraph(string fileName, string author, string initials, string comment)
{
// Use the file name and path passed in as an
// argument to open an existing Wordprocessing document.
using (WordprocessingDocument document = WordprocessingDocument.Open(fileName, true))
{
if (document.MainDocumentPart is null)
{
throw new ArgumentNullException("MainDocumentPart and/or Body is null.");
}
WordprocessingCommentsPart wordprocessingCommentsPart = document.MainDocumentPart.WordprocessingCommentsPart ?? document.MainDocumentPart.AddNewPart<WordprocessingCommentsPart>();
// Locate the first paragraph in the document.
Paragraph firstParagraph = document.MainDocumentPart.Document.Descendants<Paragraph>().First();
wordprocessingCommentsPart.Comments ??= new Comments();
string id = "0";
// Verify that the document contains a
// WordProcessingCommentsPart part; if not, add a new one.
if (document.MainDocumentPart.GetPartsOfType<WordprocessingCommentsPart>().Count() > 0)
{
if (wordprocessingCommentsPart.Comments.HasChildren)
{
// Obtain an unused ID.
id = (wordprocessingCommentsPart.Comments.Descendants<Comment>().Select(e =>
{
if (e.Id is not null && e.Id.Value is not null)
{
return int.Parse(e.Id.Value);
}
else
{
throw new ArgumentNullException("Comment id and/or value are null.");
}
})
.Max() + 1).ToString();
}
}
// Compose a new Comment and add it to the Comments part.
Paragraph p = new Paragraph(new Run(new Text(comment)));
Comment cmt =
new Comment()
{
Id = id,
Author = author,
Initials = initials,
Date = DateTime.Now
};
cmt.AppendChild(p);
wordprocessingCommentsPart.Comments.AppendChild(cmt);
// Specify the text range for the Comment.
// Insert the new CommentRangeStart before the first run of paragraph.
firstParagraph.InsertBefore(new CommentRangeStart()
{ Id = id }, firstParagraph.GetFirstChild<Run>());
// Insert the new CommentRangeEnd after last run of paragraph.
var cmtEnd = firstParagraph.InsertAfter(new CommentRangeEnd()
{ Id = id }, firstParagraph.Elements<Run>().Last());
// Compose a run with CommentReference and insert it.
firstParagraph.InsertAfter(new Run(new CommentReference() { Id = id }), cmtEnd);
}
}