編輯

共用方式為


Insert a comment into a word processing document

This topic shows how to use the classes in the Open XML SDK for Office to programmatically add a comment to the first paragraph in a word processing document.


Open the Existing Document for Editing

To open an existing document, instantiate the WordprocessingDocument class as shown in the following using statement. In the same statement, open the word processing file at the specified filepath by using the Open(String, Boolean) method, with the Boolean parameter set to true to enable editing in the document.

    using (WordprocessingDocument document =
           WordprocessingDocument.Open(filepath, true)) 
    { 
       // Insert other code here. 
    }

The using statement provides a recommended alternative to the typical .Open, .Save, .Close sequence. It ensures that the Dispose method (internal method that is used by the Open XML SDK to clean up resources) is automatically called when the closing brace is reached. The block that follows the using statement establishes a scope for the object that is created or named in the using statement, in this case document.


How the Sample Code Works

After you open the document, you can find the first paragraph to attach a comment. The code finds the first paragraph by calling the First extension method on all the descendant elements of the document element that are of type Paragraph. The First method is a member of the System.Linq.Enumerable class. The System.Linq.Enumerable class provides extension methods for objects that implement the System.Collections.Generic.IEnumerable interface.

    Paragraph firstParagraph = document.MainDocumentPart.Document.Descendants<Paragraph>().First();
    Comments comments = null;
    string id = "0";

The code first determines whether a WordprocessingCommentsPart part exists. To do this, call the MainDocumentPart generic method, GetPartsCountOfType, and specify a kind of WordprocessingCommentsPart.

If a WordprocessingCommentsPart part exists, the code obtains a new Id value for the Comment object that it will add to the existing WordprocessingCommentsPart Comments collection object. It does this by finding the highest Id attribute value given to a Comment in the Comments collection object, incrementing the value by one, and then storing that as the Id value.If no WordprocessingCommentsPart part exists, the code creates one using the AddNewPart<T>() method of the MainDocumentPart object and then adds a Comments collection object to it.

    if (document.MainDocumentPart.GetPartsCountOfType<WordprocessingCommentsPart>() > 0)
    {
        comments = 
            document.MainDocumentPart.WordprocessingCommentsPart.Comments;
        if (comments.HasChildren)
        {
            id = (comments.Descendants<Comment>().Select(e => int.Parse(e.Id.Value)).Max() + 1).ToString();
        }
    }
    else
    {
        WordprocessingCommentsPart commentPart = 
                    document.MainDocumentPart.AddNewPart<WordprocessingCommentsPart>();
       commentPart.Comments = new Comments();
       comments = commentPart.Comments;
    }

The Comment and Comments objects represent comment and comments elements, respectively, in the Open XML Wordprocessing schema. A Comment must be added to a Comments object so the code first instantiates a Comments object (using the string arguments author, initials, and comments that were passed in to the AddCommentOnFirstParagraph method).

The comment is represented by the following WordprocessingML code example. .

    <w:comment w:id="1" w:initials="User">
      ...
    </w:comment>

The code then appends the Comment to the Comments object and saves the changes. This creates the required XML document object model (DOM) tree structure in memory which consists of a comments parent element with comment child elements under it.

    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);
    comments.AppendChild(cmt);
    comments.Save();

The following WordprocessingML code example represents the content of a comments part in a WordprocessingML document.

    <w:comments>
      <w:comment … >
        …
      </w:comment>
    </w:comments>

With the Comment object instantiated, the code associates the Comment with a range in the Wordprocessing document. CommentRangeStart and CommentRangeEnd objects correspond to the commentRangeStart and commentRangeEnd elements in the Open XML Wordprocessing schema. A CommentRangeStart object is given as the argument to the InsertBefore<T>(T, OpenXmlElement) method of the Paragraph object and a CommentRangeEnd object is passed to the InsertAfter<T>(T, OpenXmlElement) method. This creates a comment range that extends from immediately before the first character of the first paragraph in the Wordprocessing document to immediately after the last character of the first paragraph.

A CommentReference object represents a commentReference element in the Open XML Wordprocessing schema. A commentReference links a specific comment in the WordprocessingCommentsPart part (the Comments.xml file in the Wordprocessing package) to a specific location in the document body (the MainDocumentPart part contained in the Document.xml file in the Wordprocessing package). The id attribute of the comment, commentRangeStart, commentRangeEnd, and commentReference is the same for a given comment, so the commentReference id attribute must match the comment id attribute value that it links to. In the sample, the code adds a commentReference element by using the API, and instantiates a CommentReference object, specifying the Id value, and then adds it to a Run object.

    firstParagraph.InsertBefore(new CommentRangeStart() 
                { Id = id }, firstParagraph.GetFirstChild<Run>());

            var cmtEnd = firstParagraph.InsertAfter(new CommentRangeEnd() 
                { Id = id }, firstParagraph.Elements<Run>().Last());

            firstParagraph.InsertAfter(new Run(new CommentReference() { Id = id }), cmtEnd);

Sample Code

The following code example shows how to create a comment and associate it with a range in a word processing document. To call the method AddCommentOnFirstParagraph pass in the path of the document, your name, your initials, and the comment text. For example, the following call to the AddCommentOnFirstParagraph method writes the comment "This is my comment." in the file "Word8.docx."

    AddCommentOnFirstParagraph(@"C:\Users\Public\Documents\Word8.docx",
     author, initials, "This is my comment.");

Following is the complete sample code in both C# and Visual Basic.

using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Wordprocessing;
using System;
using System.Linq;

AddCommentOnFirstParagraph(args[0], args[1], args[2], args[3]);

// 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/* || document.MainDocumentPart.WordprocessingCommentsPart 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);
        wordprocessingCommentsPart.Comments.Save();

        // 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);
    }
}

See also

Language-Integrated Query (LINQ)

Extension Methods (C# Programming Guide)

Extension Methods (Visual Basic)