按所有或特定作者删除字处理文档中的注释
本主题演示如何使用 Open XML SDK for Office 中的类以编程方式删除所有或特定作者在字处理文档中的批注,而无需将文档加载到 Microsoft Word。 本主题包含一个演示此任务的示例 DeleteComments 方法。
DeleteComments 方法
您可以使用 DeleteComments 方法从字处理文档中删除所有注释,或仅删除某个特定作者编写的注释。 如下面的代码所示,该方法接受两个参数:一个指示要修改的文档的名称(字符串),(可选)一个指示要删除其注释的作者的姓名(字符串)。 如果您提供作者姓名,那么代码将删除指定作者编写的注释。 如果您不提供作者姓名,代码将删除所有注释。
// Delete comments by a specific author. Pass an empty string for the
// author to delete all comments, by all authors.
public static void DeleteComments(string fileName,
string author = "")
调用 DeleteComments 方法
若要调用 DeleteComments 方法,请提供所需参数,如下面的代码所示。
DeleteComments(@"C:\Users\Public\Documents\DeleteComments.docx",
"David Jones");
代码的工作方式
下面的代码首先使用 WordprocessingDocument.Open 方法打开文档,并指示应打开文档供读/写访问(最后的 true 参数值)。 接下来,代码在从字处理文档的 MainDocumentPart 属性检索对 main 文档部件的引用后,使用 main 文档部件的 WordprocessingCommentsPart 属性检索对注释部件的引用。 如果注释部件缺失,则无需继续处理,因为没有可删除的注释。
// Get an existing Wordprocessing document.
using (WordprocessingDocument document =
WordprocessingDocument.Open(fileName, true))
{
// Set commentPart to the document WordprocessingCommentsPart,
// if it exists.
WordprocessingCommentsPart commentPart =
document.MainDocumentPart.WordprocessingCommentsPart;
// If no WordprocessingCommentsPart exists, there can be no
// comments. Stop execution and return from the method.
if (commentPart == null)
{
return;
}
// Code removed here…
}
创建注释列表
接下来,代码将执行两个任务:创建要删除的所有注释的列表,以及创建与要删除的批注对应的注释 ID 列表。 给定这些列表,代码既可以从包含注释的注释部件中删除注释,也可以从文档部件中删除对批注的引用。以下代码首先检索 Comment 元素列表。 为了检索列表,它将 commentPart 变量公开的 Elements 集合转换为 Comment 对象的列表。
List<Comment> commentsToDelete =
commentPart.Comments.Elements<Comment>().ToList();
到目前为止,注释列表已包含所有注释。 如果 Author 参数不是空字符串,则下面的代码会将列表限制为仅包括其 Author 属性与您提供的参数匹配的那些注释。
if (!String.IsNullOrEmpty(author))
{
commentsToDelete = commentsToDelete.
Where(c => c.Author == author).ToList();
}
在删除任何注释之前,代码将检索注释 ID 值的列表,以便稍后代码从文档部件中删除匹配的元素。 对 Select 方法的调用有效地投影注释列表,检索包含所有注释 ID 值的字符串的 IEnumerable<T> 。
IEnumerable<string> commentIds =
commentsToDelete.Select(r => r.Id.Value);
删除注释并保存部分
获得 commentsToDelete 集合后,下面的代码循环访问所有需要删除的注释,并执行删除。 然后代码保存注释部件。
// Delete each comment in commentToDelete from the
// Comments collection.
foreach (Comment c in commentsToDelete)
{
c.Remove();
}
// Save the comment part changes.
commentPart.Comments.Save();
删除文档中的注释引用
尽管此时代码已成功删除了所有注释,但是这还不够。 代码还必须从文档部件中删除对注释的引用。 此操作需要三个步骤,因为注释引用包括 CommentRangeStart、 CommentRangeEnd 和 CommentReference 元素,并且代码必须删除每个注释的所有三个步骤。 在执行任何删除之前,代码首先检索对主文档部件根元素的引用,如下面的代码所示。
Document doc = document.MainDocumentPart.Document;
获得对文档元素的引用后,下面的代码反复执行三次删除操作,对必须删除的每个不同元素分别执行一次。 在每种情况下,代码都会查找 (CommentRangeStart、 CommentRangeEnd 或 CommentReference) 的正确类型的所有后代,并将列表限制为 Id 属性值包含在 要删除的注释 ID 列表中。 获得要删除的元素列表后,代码会依次删除每个元素。 最后,代码保存文档并随即结束。
// Delete CommentRangeStart for each
// deleted comment in the main document.
List<CommentRangeStart> commentRangeStartToDelete =
doc.Descendants<CommentRangeStart>().
Where(c => commentIds.Contains(c.Id.Value)).ToList();
foreach (CommentRangeStart c in commentRangeStartToDelete)
{
c.Remove();
}
// Delete CommentRangeEnd for each deleted comment in the main document.
List<CommentRangeEnd> commentRangeEndToDelete =
doc.Descendants<CommentRangeEnd>().
Where(c => commentIds.Contains(c.Id.Value)).ToList();
foreach (CommentRangeEnd c in commentRangeEndToDelete)
{
c.Remove();
}
// Delete CommentReference for each deleted comment in the main document.
List<CommentReference> commentRangeReferenceToDelete =
doc.Descendants<CommentReference>().
Where(c => commentIds.Contains(c.Id.Value)).ToList();
foreach (CommentReference c in commentRangeReferenceToDelete)
{
c.Remove();
}
// Save changes back to the MainDocumentPart part.
doc.Save();
示例代码
以下是使用 C# 和 Visual Basic 编写的完整示例代码。
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Wordprocessing;
using System;
using System.Collections.Generic;
using System.Linq;
DeleteComments(args[0], args[1]);
// Delete comments by a specific author. Pass an empty string for the
// author to delete all comments, by all authors.
static void DeleteComments(string fileName, string author = "")
{
// Get 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 WordprocessingCommentsPart is null.");
}
// Set commentPart to the document WordprocessingCommentsPart,
// if it exists.
WordprocessingCommentsPart commentPart =
document.MainDocumentPart.WordprocessingCommentsPart;
// If no WordprocessingCommentsPart exists, there can be no
// comments. Stop execution and return from the method.
if (commentPart is null)
{
return;
}
// Create a list of comments by the specified author, or
// if the author name is empty, all authors.
List<Comment> commentsToDelete =
commentPart.Comments.Elements<Comment>().ToList();
if (!String.IsNullOrEmpty(author))
{
commentsToDelete = commentsToDelete.
Where(c => c.Author == author).ToList();
}
IEnumerable<string?> commentIds =
commentsToDelete.Where(r => r.Id is not null && r.Id.HasValue).Select(r => r.Id?.Value);
// Delete each comment in commentToDelete from the
// Comments collection.
foreach (Comment c in commentsToDelete)
{
c.Remove();
}
// Save the comment part change.
commentPart.Comments.Save();
Document doc = document.MainDocumentPart.Document;
// Delete CommentRangeStart for each
// deleted comment in the main document.
List<CommentRangeStart> commentRangeStartToDelete =
doc.Descendants<CommentRangeStart>().
Where(c => c.Id is not null && c.Id.HasValue && commentIds.Contains(c.Id.Value)).ToList();
foreach (CommentRangeStart c in commentRangeStartToDelete)
{
c.Remove();
}
// Delete CommentRangeEnd for each deleted comment in the main document.
List<CommentRangeEnd> commentRangeEndToDelete =
doc.Descendants<CommentRangeEnd>().
Where(c => c.Id is not null && c.Id.HasValue && commentIds.Contains(c.Id.Value)).ToList();
foreach (CommentRangeEnd c in commentRangeEndToDelete)
{
c.Remove();
}
// Delete CommentReference for each deleted comment in the main document.
List<CommentReference> commentRangeReferenceToDelete =
doc.Descendants<CommentReference>().
Where(c => c.Id is not null && c.Id.HasValue && commentIds.Contains(c.Id.Value)).ToList();
foreach (CommentReference c in commentRangeReferenceToDelete)
{
c.Remove();
}
// Save changes back to the MainDocumentPart part.
doc.Save();
}
}