按所有或特定作者删除字处理文档中的注释
本主题演示如何使用 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.
static void DeleteComments(string fileName, string author = "")
调用 DeleteComments 方法
若要调用 DeleteComments
方法,请提供所需的参数,如以下代码所示。
if (args is [{ } fileName, { } author])
{
DeleteComments(fileName, author);
}
else if (args is [{ } fileName2])
{
DeleteComments(fileName2);
}
代码的工作方式
以下代码首先打开文档,使用 WordprocessingDocument.Open 方法,并指示应打开文档以 (最终 true
参数值) 进行读/写访问。 接下来,代码在从MainDocumentPart字处理文档的 属性检索对 main 文档部件的引用后,使用 WordprocessingCommentsPart main 文档部件的 属性检索对注释部件的引用。 如果注释部件缺失,则无需继续处理,因为没有可删除的注释。
// Get an existing Wordprocessing document.
using (WordprocessingDocument document = WordprocessingDocument.Open(fileName, true))
{
if (document.MainDocumentPart is null)
{
throw new ArgumentNullException("MainDocumentPart 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;
}
创建注释列表
接下来,代码将执行两个任务:创建要删除的所有注释的列表,以及创建与要删除的批注对应的注释 ID 列表。 给定这些列表,代码既可以从包含注释的注释部件中删除注释,也可以从文档部件中删除对批注的引用。以下代码首先检索元素列表 Comment 。 为了检索列表,它将变量公开的Elements()commentPart
集合转换为对象的列表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 调用有效地投影注释列表,检索 IEnumerable<T> 包含所有注释 ID 值的字符串的 。
IEnumerable<string?> commentIds = commentsToDelete.Where(r => r.Id is not null && r.Id.HasValue).Select(r => r.Id?.Value);
删除注释并保存部分
给定集合 commentsToDelete
后,以下代码将遍历需要删除的所有注释并执行删除。
// Delete each comment in commentToDelete from the
// Comments collection.
foreach (Comment c in commentsToDelete)
{
if (c is not null)
{
c.Remove();
}
}
删除文档中的注释引用
尽管此时代码已成功删除了所有注释,但是这还不够。 代码还必须从文档部件中删除对注释的引用。 此操作需要三个步骤,因为注释引用包括 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 => 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();
}
示例代码
以下是使用 C# 和 Visual Basic 编写的完整示例代码。
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Wordprocessing;
using System;
using System.Collections.Generic;
using System.Linq;
// 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)
{
throw new ArgumentNullException("MainDocumentPart 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)
{
if (c is not null)
{
c.Remove();
}
}
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();
}
}
}