在字处理文档中插入注释

本主题演示如何使用 Open XML SDK for Office 中的类以编程方式向字处理文档中的第一段添加注释。


打开现有文档以进行编辑

若要打开现有文档,请实例化 类, WordprocessingDocument 如以下 using 语句所示。 在同一语句中,使用 Open(String, Boolean) 方法打开指定 filepath 处的字处理文件,并将布尔参数设置为 true 以启用文档中的编辑。

using (WordprocessingDocument document = WordprocessingDocument.Open(fileName, true))

在 v3.0.0+ 中, Close() 已删除 方法,转而依赖于 using 语句。 它确保在 Dispose() 到达右大括号时自动调用 方法。 using 语句后面的块为 using 语句中创建或指定的对象设定范围。 WordprocessingDocument由于 Open XML SDK 中的 类会自动保存并关闭对象作为其IDisposable实现的一using部分,并且由于Dispose()在退出块时会自动调用,因此无需显式调用 Save()Dispose() ,只要使用 语句。


示例代码的工作方式

打开文档后,可以找到要附加文档的第一个段落。 代码通过对类型 Paragraph为 的文档元素的所有后代元素调用First扩展方法来查找第一个段落。 方法 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存在,则代码将为Comment对象获取一个新Id值,该对象将添加到现有WordprocessingCommentsPartComments集合对象。 为此,Id它会在集合对象中Comments查找给定的最高Comment属性值,将值递增 1,然后将其存储为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();
    }
}

CommentComments 对象分别表示 Open XML Wordprocessing 架构中的注释和注释元素。 Comment必须将 添加到 对象中,Comments以便代码首先使用传递给 AddCommentOnFirstParagraph 方法的字符串参数 authorinitialscomments 实例化Comments对象 () 。

注释由以下 WordprocessingML 代码示例表示。 .

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

然后,代码会将 Comment 追加到 对象。Comments 这会在内存中创建所需的 XML 文档对象模型 (DOM) 树结构,该结构由父元素和comment子元素组成comments

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 文档中的区域相关联。 CommentRangeStartCommentRangeEnd 对象对应于 commentRangeStart Open XML Wordprocessing 架构中的 和 commentRangeEnd 元素。 对象CommentRangeStart作为 对象的 方法Paragraph的参数InsertBefore提供,CommentRangeEnd对象传递给 InsertAfter 方法。 这将创建一个注释区域,从 Wordprocessing 文档中第一个段落的第一个字符的紧前面延伸到第一个段落的最后一个字符的紧后面。

对象 CommentReference 表示 commentReference Open XML Wordprocessing 架构中的元素。 commentReference 将 Wordprocessing 包) (Comments.xml 部件中的特定注释 WordprocessingCommentsPart 链接到文档正文中的特定位置, (MainDocumentPart wordprocessing 包) Document.xml 文件中包含的部件。 给定注释的 comment、commentRangeStart、commentRangeEnd 和 commentReference 的 id 属性是相同的,这样 commentReference id 属性必须匹配其链接到的 comment id 属性值。 在示例中,代码使用 API 添加 commentReference 元素,并实例化对象 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 ,请传入文档的路径、姓名、姓名缩写和注释文本。

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

另请参阅