ワープロ ドキュメント内のすべての作成者または特定の作成者によるコメントを削除する
このトピックでは、Open XML SDK for Office のクラスを使用して、文書を Microsoft Wordに読み込む必要なく、すべてのユーザーまたは特定の作成者によるコメントをプログラムで削除する方法について説明します。 このタスクを示すメソッド DeleteComments
例が含まれています。
DeleteComments メソッド
DeleteComments
メソッドを使用して、ワープロ ドキュメントからすべてのコメントを削除するか、特定の作成者によって書き込まれたコメントのみを削除できます。 次のコードに示すように、 メソッドは、変更するドキュメントの名前 (文字列) を示す 2 つのパラメーターを受け取り、必要に応じて、コメントを削除する作成者の名前 (文字列) を受け取ります。 作成者名を指定すると、指定した作成者によって書き込まれたコメントが削除されます。 作成者名を指定しない場合、コードはすべてのコメントを削除します。
// 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 プロパティからメインドキュメント パーツへの参照を取得した後、メイン ドキュメント パーツの WordprocessingCommentsPart プロパティを使用して、コメント パーツへの参照を取得します。 コメント パーツがない場合、削除するコメントが存在する可能性がないので、続行する意味がありません。
// 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 のリストの作成という 2 つのタスクを実行します。 これらのリストを使用すると、コードはコメントを含むコメント パーツからコメントを削除し、ドキュメント パーツからコメントへの参照を削除できます。次のコードは、まず 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.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 の各要素が含まれており、コードではコメントごとに 3 つすべてを削除する必要があるため、このアクションには 3 つの手順が必要です。 削除を実行する前に、次のコードに示すように、最初にメイン ドキュメント パーツのルート要素への参照を取得します。
Document doc = document.MainDocumentPart.Document;
ドキュメント要素への参照を基に、以下のコードは削除ループを、削除する必要がある要素ごとに 1 回ずつ、合計 3 回実行します。 いずれの場合も、コードは正しい型 (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();
}
}
}