Поделиться через


Вставьте комментарий в текстовый документ

В этом разделе показано, как использовать классы в пакете 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 объекта, который он добавит к существующему WordprocessingCommentsPartComments объекту коллекции. Для этого в объекте коллекции обнаруживается наибольшее 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);
    }
}

См. также