次の方法で共有


ワープロ ドキュメントにコメントを挿入する

このトピックでは、Open XML SDK for Office のクラスを使用して、プログラムによってワープロ ドキュメントの最初の段落にコメントを追加する方法について説明します。


既存のドキュメントを編集用に開く

既存のドキュメントを開くには、次の using ステートメントに示すように、WordprocessingDocument クラスをインスタンス化します。 同じステートメントで、Open(String, Boolean) メソッドを使用して、指定したファイルパスでワープロ ファイルを開き、Boolean パラメーターを true に設定してドキュメント内の編集を有効にします。

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

v3.0.0 以降では、using ステートメントに依存することを優先して、Close() メソッドが削除されました。 これにより、閉じかっこに達したときに、 Dispose() メソッドが自動的に呼び出されます。 using ステートメントに続くブロックは、using ステートメントで作成または名前付けされたオブジェクトのスコープを確立します。 Open XML SDK の WordprocessingDocument クラスは、IDisposable実装の一部としてオブジェクトを自動的に保存および閉じます。また、ブロックを終了するとDispose()が自動的に呼び出されるため、using ステートメントを使用する限り、明示的に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 パーツが存在する場合、コードは、既存のWordprocessingCommentsPartComments コレクション オブジェクトに追加するComment オブジェクトの新しいId値を取得します。 これは、Comments コレクション オブジェクト内のCommentに与えられた最も高いId属性値を検索し、値を 1 ずつインクリメントし、それをId値として格納することによって行われます。WordprocessingCommentsPart部分が存在しない場合、コードは、MainDocumentPart オブジェクトの AddNewPart メソッドを使用してパーツを作成し、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();
    }
}

Comment オブジェクトと Comments オブジェクトは、Open XML Wordprocessing スキーマのコメント要素とコメント要素をそれぞれ表します。 CommentComments オブジェクトに追加する必要があります。コードは、最初に (AddCommentOnFirstParagraph メソッドに渡された文字列引数authorinitialscommentsを使用して) Comments オブジェクトをインスタンス化します。

コメントは、次の WordprocessingML コード例で表されます。 .

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

その後、コードは Comments オブジェクトにCommentを追加します。 これにより、必要な 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 ドキュメント内の範囲に関連付けます。 CommentRangeStart および CommentRangeEnd オブジェクトは、Open XML Wordprocessing スキーマの commentRangeStart 要素と commentRangeEnd 要素に対応します。 CommentRangeStart オブジェクトは、Paragraph オブジェクトの InsertBefore メソッドの引数として指定され、CommentRangeEnd オブジェクトは InsertAfter メソッドに渡されます。 これによって、コメントの範囲が、Wordprocessing ドキュメントの最初の段落で、最初の文字の直前から最後の文字の直後まで作成されます。

CommentReference オブジェクトは、Open XML Wordprocessing スキーマのcommentReference要素を表します。 commentReference は、 WordprocessingCommentsPart パーツ内の特定のコメント (Wordprocessing パッケージ内の Comments.xml ファイル) を、文書本文 (Wordprocessing パッケージの Document.xml ファイルに含まれる MainDocumentPart 部分) の特定の場所にリンクします。 コメント、commentRangeStart、commentRangeEnd、および commentReference の id 属性は、特定のコメントで同じであるため、commentReference id 属性は、リンク先のコメント id 属性値と一致する必要があります。 サンプルでは、API を使用して commentReference 要素を追加し、Id値を指定して CommentReference オブジェクトをインスタンス化し、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);
    }
}

関連項目