6

次のコードを使用して、Word 文書のブックマークにアクセスできました。

var res = from bm in mainPart.Document.Body.Descendants<BookmarkStart>()
                              where bm.Name == "BookmarkName"
                              select bm;

このブックマークの後に段落と表を挿入したいと思います。それ、どうやったら出来るの?(サンプルコードをいただければ幸いです)

4

1 に答える 1

31

コード

ブックマークを取得したら、その親要素にアクセスして、その後に他のアイテムを追加できます。

using (WordprocessingDocument document = WordprocessingDocument.Open(@"C:\Path\filename.docx", true))
{
    var mainPart = document.MainDocumentPart;
    var res = from bm in mainPart.Document.Body.Descendants<BookmarkStart>()
              where bm.Name == "BookmarkName"
              select bm;
    var bookmark = res.SingleOrDefault();
    if (bookmark != null)
    {
        var parent = bookmark.Parent;   // bookmark's parent element

        // simple paragraph in one declaration
        //Paragraph newParagraph = new Paragraph(new Run(new Text("Hello, World!")));

        // build paragraph piece by piece
        Text text = new Text("Hello, World!");
        Run run = new Run(new RunProperties(new Bold()));
        run.Append(text);
        Paragraph newParagraph = new Paragraph(run);

        // insert after bookmark parent
        parent.InsertAfterSelf(newParagraph);

        var table = new Table(
        new TableProperties(
            new TableStyle() { Val = "TableGrid" },
            new TableWidth() { Width = 0, Type = TableWidthUnitValues.Auto }
            ),
            new TableGrid(
                new GridColumn() { Width = (UInt32Value)1018U },
                new GridColumn() { Width = (UInt32Value)3544U }),
        new TableRow(
            new TableCell(
                new TableCellProperties(
                    new TableCellWidth() { Width = 0, Type = TableWidthUnitValues.Auto }),
                new Paragraph(
                    new Run(
                        new Text("Category Name"))
                )),
            new TableCell(
                new TableCellProperties(
                    new TableCellWidth() { Width = 4788, Type = TableWidthUnitValues.Dxa }),
                new Paragraph(
                    new Run(
                        new Text("Value"))
                ))
        ),
        new TableRow(
            new TableCell(
                new TableCellProperties(
                    new TableCellWidth() { Width = 0, Type = TableWidthUnitValues.Auto }),
                new Paragraph(
                    new Run(
                        new Text("C1"))
                )),
            new TableCell(
                new TableCellProperties(
                    new TableCellWidth() { Width = 0, Type = TableWidthUnitValues.Auto }),
                new Paragraph(
                    new Run(
                        new Text("V1"))
                ))
        ));

        // insert after new paragraph
        newParagraph.InsertAfterSelf(table);
    }

    // close saves all parts and closes the document
    document.Close();
}

上記のコードはそれを行う必要があります。ただし、いくつかの特殊な状況について説明します。

ブックマークの親要素の後に挿入を試みることに注意してください。ブックマークがたまたまテーブル内の段落の一部である場合、どのような動作が予想されますか? そのテーブル内で、その直後に新しい段落とテーブルを追加する必要がありますか? それとも、そのテーブルの後にそれを行うべきですか?

上記の質問がなぜ重要なのか疑問に思われるかもしれません。それはすべて、挿入が発生する場所によって異なります。ブックマークの親がテーブル内にある場合、現在、上記のコードはテーブル内にテーブルを配置しようとします。それは問題ありませんが、無効な OpenXml 構造が原因でエラーが発生する可能性があります。その理由は、挿入されたテーブルが元のテーブルの TableCell の最後の要素であった場合、TableCell の終了タグの後に Paragraph 要素を追加する必要があるためです。MS Word でドキュメントを開こうとしたときにこの問題が発生した場合は、すぐにこの問題に気付くでしょう。

解決策は、実際にテーブル内で挿入を実行しているかどうかを判断することです。

そのために、上記のコードに (親変数の後に) 追加できます。

    var parent = bookmark.Parent;   // bookmark's parent element

    // loop till we get the containing element in case bookmark is inside a table etc.
    // keep checking the element's parent and update it till we reach the Body
    var tempParent = bookmark.Parent;
    bool isInTable = false;
    while (tempParent.Parent != mainPart.Document.Body)
    {
        tempParent = tempParent.Parent;
        if (tempParent is Table && !isInTable)
            isInTable = true;
    }

    // ... 

    newParagraph.InsertAfterSelf(table);  // from above sample
    // if bookmark is in a table, add a paragraph after table
    if (isInTable)
        table.InsertAfterSelf(new Paragraph());

これにより、エラーの発生が防止され、有効な OpenXml が提供されます。前の質問に「はい」と答え、上記のコードのようにテーブル内ではなく親テーブルの後に挿入を実行したい場合は、while ループのアイデアを使用できます。その場合、上記の問題はもはや問題ではなく、そのループとブール値を次のものに置き換えることができます。

    var parent = bookmark.Parent;   // bookmark's parent element
    while (parent.Parent != mainPart.Document.Body)
    {
        parent = parent.Parent;
    }

これにより、親が Body レベルでメインの包含要素になるまで、親が再割り当てされ続けます。そのため、ブックマークがテーブル内の段落にある場合、テーブルの親が本文であるため、ブックマークは段落からテーブルセル、テーブル行、テーブルに移動し、そこで停止します。その時点で、親 = テーブル要素であり、その後に挿入できます。

元の意図に応じて、いくつかの異なるアプローチをカバーする必要があります。試した後、説明が必要な場合はお知らせください。

ドキュメントリフレクター

どうやってGridColumn.Width値を決定したのか疑問に思われるかもしれません。テーブルを作成し、Document Reflector ツールを使用して取得しました。Open Xml SDK をインストールすると、生産性ツール (インストールした場合) はC:\Program Files\Open XML Format SDK\V2.0\tools(または同様の場所) に配置されます。

*.docx 形式 (または任意の Open Xml 形式のドキュメント) がどのように機能するかを学習する最善の方法は、Document Reflector ツールを使用して既存のファイルを開くことです。ドキュメント パーツをナビゲートし、複製するアイテムを見つけます。このツールは、ドキュメント全体を生成するために使用された実際のコードを表示します。これは、アプリケーションにコピーして貼り付けて、同様の結果を生成できるコードです。通常はすべての参照 ID を無視できます。見て、試してみて、その感触をつかむ必要があります。

前述したように、上記のテーブル コードはサンプル ドキュメントから改作されました。単純なテーブルを docx に追加し、それをツールで開き、ツールによって生成されたコードをコピーしました (余分なものを削除してクリーンアップしました)。これにより、テーブルを追加するための実用的なサンプルが得られました。

書式設定された表やスタイル付きの段落など、何かを生成するコードの書き方を知りたい場合に特に役立ちます。

スクリーンショットと、SDK に含まれるその他のツールに関する情報については、このリンクを参照してください: Open XML SDK 2.0 の紹介

コード スニペット

Open Xml のコード スニペットにも興味があるかもしれません。スニペットのリストについては、このブログ投稿を確認してください。2007 Office System サンプル: Open XML Format SDK 2.0 Code Snippet for Visual Studio 2008からダウンロードできます。

インストールしたら、[ツール] | [ツール] から追加します。コード スニペット マネージャー メニュー。言語として C# を選択し、[追加] ボタンをクリックして、PersonalFolder\Visual Studio 2008\Code Snippets\Visual C#\Open XML SDK 2.0 for Microsoft Officeに移動して、それらを追加します。コードから右クリックして「スニペットの挿入」を選択し、必要なものを選択します。

于 2009-10-25T23:45:18.033 に答える