2

MS OpenXml Sdkを使用して、テンプレートストリームを結果ストリームにコピーし、次のコードを使用して本文の最後に動的テキスト(wp >> wr >> wt)を追加することができました。

var templateStream = File.OpenRead(templatePath);
templateStream.CopyTo(resultStream);

using (var resultPackage = WordprocessingDocument.Open(resultStream, true))
{
     var document = resultPackage.MainDocumentPart.Document;

     var body = document.Body;

     // Add new text.
     var para = body.AppendChild(new Paragraph());
     var run = para.AppendChild(new Run());
     run.AppendChild(new Text(firstName));
     document.Save();
}

次の論理的な手順は、以下のコードのように、resultStreamのテキストボックスの内部テキストをfirstNameに置き換えることでした。

// replacing code in using statement from above
var document = resultPackage.MainDocumentPart.Document;
var textbox = document.Descendants<TextBox>().First();
const string firstNametag = "<<IH.FirstName>>";
if (textbox.InnerText.Contains(firstNametag))
{
   var textboxContent = textbox.Elements<TextBoxContent>().First();
   textboxContent.RemoveAllChildren();
   var paragraph = textboxContent.AppendChild(new Paragraph());
   var run = paragraph.AppendChild(new Run());
   run.AppendChild(new Text(firstName));
}
document.Save();

最初の例では、いくつかの追加コードを使用して、結果ストリームがdocxに適切にシリアル化され、Wordで表示したときにfirstNameが本文の末尾に追加されます。2番目の例では、デバッガーでさらに調べたところ、textboxContentの子が上記の変更を反映していることが示されていても、テキストボックスとその内容は同じままです。

私はOpenXML開発に不慣れなので、明らかなことがあれば指摘してください。

4

1 に答える 1

4

ああ、すごい、私はこれの周りの全体像を理解しているとは思いたくありませんが、ここにそれを簡単に突き刺します。openXmlの経験が豊富な人は、チャイムを鳴らしてください...

docxにWordでテキストボックスを作成すると、document.xmlファイルは次のマークアップを取得します。

<w.r>
    <mc.AlertnateContent>
        <mc.Choice Requires="wps">
            <wps:txbx>
                <w:txbxContent>
                    <w:r>
                        <w.t>
                            Text Goes Here
                        </w.t>
                    </w.r>
                </w:txbxContent>
            </wps:txbx>
        </mc.Choice>
        <mc.Fallback>
            <v.textbox>
                <w:txbxContent>
                    <w:r>
                        <w.t>
                            Text Goes Here
                        </w.t>
                    </w.r>
                </w:txbxContent>
            </v.textbox>
        </mc.Fallback>
    </mc.AlertnateContent>
</w.r>

mc.AlternateContent、mc.Choice、およびmc.Fallbackタグに注意してください。これらは一体何ですか?誰かが私が出会ったブログ記事にこのように書いた-

「私は理解していますが、私の言葉を福音とは見なさないでください。AlternateContentはどこにでも表示でき、消費するアプリケーションが処理できる場合は拡張機能を含めるためのメカニズムを提供し、処理できない場合はフォールバックを提供します。」-Tony Jollans- http: //social.msdn.microsoft.com/Forums/en-US/worddev/thread/f8a5c277-7049-48c2-a295-199d2914f4ba/

私の場合、フォールバックテキストボックス(wps.txbxではなくv.txbx)のみを変更していました。これは、TextBoxオブジェクトへの依存関係についてResharperがDocumentFormat.OpenXml.Vml名前空間をインポートするように要求したときに事故が発生したためです。すでに含まれている名前空間の1つであるDocumentFormat.OpenXml.PackagingまたはDocumentFormat.OpenXml.WordprocessingにTextbox定義がない理由はわかりませんが、それはこの質問の範囲を超えています。言うまでもなく、これを実現し、コードを更新して2つの共通のw.txbxContentを探すと、やりたいことが達成されました。

リファクタリングを使用して更新されたコードを次に示します。元の質問のusingステートメントでReplaceTagメソッドを呼び出し、文字列の代わりにモデルオブジェクトを指定します。また、便宜上、tagToValueSelectorディクショナリを使用してください。

private void ReplaceTags(Document document, SomeModel model)
{
    var textBoxContents = document.Descendants<TextBoxContent>().ToList();
    foreach (var textBoxContent in textBoxContents)
    {
        ReplaceTag(textBoxContent, model);
    }
}

private void ReplaceTag(TextBoxContent textBoxContent, SomeModel model)
{
    var tag = textBoxContent.InnerText.Trim();
    if (!tagsTomValues.ContainsKey(tag)) return;
    var valueSelector = tagsTomValues[tag];
    textBoxContent.RemoveAllChildren();
    var paragraph = textBoxContent.AppendChild(new Paragraph());
    var run = paragraph.AppendChild(new Run());
    run.AppendChild(new Text(valueSelector(model)));
}

// called in the ctor
private static void IntializeTags(IDictionary<string, Func<SomeModel, string>> dictionary)
{
    dictionary.Add("<<IH.Name>>", m => string.Format("{0} {1}", m.FirstName, m.LastName));
}

ハッピーopenXmling:)

于 2012-05-03T03:53:00.633 に答える