2017-09-26 14 views
1

私はIIS 7.5上で実行されているASP.NET 4.5 Webフォームアプリケーションを持っています。OpenXMLでword docを生成

私はカスタムフォームを持っているページの1つからワードドキュメントを生成しようとしています。

マージフィールドを含むワードドキュメントテンプレートをアップロードしました。 コードの背後に、SQLデータベースクエリに基づいてマージフィールドを設定します。

一部のマージフィールドでは、複数行のテキストを挿入する必要があります。それらの中には弾丸リストさえあるものもあります。これらのテキスト断片は私がSQLに保存することはできませんので、ブックマークを使って別の単語文書に追加しました。ただ要約するので

、:

Template.dotx - >ブックマークでマークされたテキストの断片が含まれています - > [差し込みフィールド

Data.docxが含まれています。

私は、Template.dotxのマージフィールドをOpenXMLで置き換えることができましたが、ブックマークからマージフィールドにデータを取得する方法が見つかりません。

これはInteropでうまくいきますが、サーバーにアップロードしたときに問題が発生してOpenXMLに切り替えました。

これは私がこれまでにしようとしているものです:

private string GetBookmarkData(WordprocessingDocument secondWordDoc, string bookmarkKey) 
     { 
      string returnVal = ""; 
      foreach (BookmarkStart bookmarkStart in secondWordDoc.MainDocumentPart.RootElement.Descendants<BookmarkStart>()) 
      { 
       if(bookmarkStart.Name == bookmarkKey) 
       { 
        foreach(Run run in bookmarkStart.Parent.Descendants<Run>()) 
        { 
         returnVal += run.Descendants<Text>().FirstOrDefault().Text + "<br/>"; 
        } 
       } 
      } 
      return returnVal; 
     } 


protected void PrintBtn_Click(object sender, EventArgs e) 
{ 
      string mainTemplate = Server.MapPath("~/MyFolder/Template.dotx"); 
      string savePath = Server.MapPath("~/SaveFolder/Final.docx"); 

      File.Copy(mainTemplate, savePath); 
      using(WordprocessingDocument firstDoc = WordprocessingDocument.Open(savePath, true)) 
      { 
       using (WordprocessingDocument secondDoc = WordprocessingDocument.Open(Server.MapPath("~/MyFolder/Data.docx"), true)) 
       { 
        foreach (FieldCode field in firstDoc.MainDocumentPart.RootElement.Descendants<FieldCode>()) 
        { 
         var fieldNameStart = field.Text.LastIndexOf(" MERGEFIELD", System.StringComparison.Ordinal); 
         String fieldText = field.InnerText; 
         if (fieldText.StartsWith(" MERGEFIELD")) 
         { 
          Int32 endMerge = fieldText.IndexOf("\\"); 
          Int32 fieldNameLength = fieldText.Length - endMerge; 
          String fieldName = fieldText.Substring(11, endMerge - 11); 
          fieldName = fieldName.Trim(); 
          string autoFill = ""; 

           switch (fieldName) 
           { 
            case "MergeField1": 
             autoFill = mergefield_1; 
             break; 
            case "MergeField2": 
             autoFill = mergefield_2; 
             break; 
            case "MergeField3": 
             autoFill = GetBookmarkData(secondDoc, "Bookmark1"); 
             break; 
            case "MergeField4": 
             autoFill = GetBookmarkData(secondDoc, "Bookmark2"); 
             break; 
            case "MergeField5": 
             autoFill = GetBookmarkData(secondDoc, "Bookmark3"); 
             break; 
           } 
         } 

         foreach (Run run in firstDoc.MainDocumentPart.Document.Descendants<Run>()) 
         { 
          foreach (Text txtFromRun in run.Descendants<Text>().Where(a => a.Text == "«" + fieldName + "»")) 
          { 
           txtFromRun.Text = autoFill; 
          } 
         } 
        } 
       } 
      } 

    firstDoc.ChangeDocumentType(WordprocessingDocumentType.Document); 
    firstDoc.MainDocumentPart.Document.Save(); 
} 

}

だから、これは何をするのでしょうか?

ボタンをクリックすると、メソッドPrintBtn_Clickが呼び出されます。いくつかのSQLマジック(これには含まれていない)を実行した後、各マージフィールドを満たすいくつかの変数を初期化します。この例は短く編集されたバージョンです。オリジナルははるかに大きいです。このコードを使用して、マージフィールドを作成しました。それは素晴らしい作品です。しかし、方法: `

string GetBookmarkData(WordprocessingDocument secondWordDoc, string bookmarkKey)` 

それは本当に何をするかはしません。それはData.docxに移動し、指定したブックマークからすべてのテキストを取得する必要があります。それは、箇条書きや奇妙な書式のない行だけを返します。

私はInteropを使用して同じプロセスを使用しましたが、問題はありませんでした。 OpenXMLでこれをどうすればできますか?弾丸のある行は別のXMLに格納されていますか?

私は、BookmarkStartとBookmarkEndの間のすべてのRunを取得し、そこからTextを取得しようとしました。

何か助けていただければ幸いです。 ありがとうございました!

更新

secondDocが実際にData.docxで、次のようになります。

Bookmark1 

• Text-Information 1 (This is just an example) 
• Text-Information 2 (This is just an example) 
• Text-Information 3 (This is just an example) 
• Text-Information 4 (This is just an example) 

Bookmark2 

This is a list of multiple items: 
Item 1        x.000,00 
Item 2        x.000,00 
Item 3        x.000,00 
Item 4        x.000,00 
Item 5        000,00 
This is the conclusion for this list. 

Following is a list of other multiple items: 
Item 1        x.000,00 
Item 2        x.000,00 
Item 3        x.000,00 
Item 4        x.000,00 
Item 5        000,00 
This is the conclusions for this list 


Bookmark3 

a) Another example of text that needs to go in the mergefield: 
• Article 1 xxxx Quantity/Producer etc 
• Article 2 xxxx Quantity/Producer etc 
Some details about this block of text that is not relevant but I need to insert it in the merge field as well 

ので、テキスト全体が "Bookmark1"/"Bookmark2"/"Bookmark3" の後に、行く必要特定のラジオボタンが押された場合は、その特定のマージフィールドに表示されます。私はこれらのテキストブロックをブックマークしました。上記のように、弾丸のない行を挿入するだけです。たとえば、Bookmark2に対応するマージフィールドは、 "これは複数のアイテムのリストです:"のみを受け取ります。

まず

+0

私たちはsecondDocを参照する必要があります。構造を表示できたら、コードのトラブルシューティングに役立てることができます。 – Taterhead

+0

私は最初の投稿を更新しました。私はそれがはっきりしていることを望むありがとうございました! – Cosmos24Magic

+0

明らかですが、十分明確ではありません。正確なxml構造を調べるためには、実際の文書が必要です。これは、あなたのメソッドが正しい値を返さない理由を判断するのに役立ちます。ファイルを共有したくない、または共有できない場合、私は理解しています。あなたがする必要があるのは、Open XMLの生産性ツール(https://www.microsoft.com/en-us/download/details.aspx?id=30425)をダウンロードし、ドキュメントを開いてXML構造を調べることです。次に、構造に従ってGetBookmarkDataメソッドを書き換えます。あなたが文書を投稿すれば、あなたのためにそれを行います;) – Taterhead

答えて

1

は、ドキュメントとあなたのコードを見てみると、私はあなたの問題の源となる可能性が二つの場所を参照してください

Bookmark1を含むあなたのSecondTemplate.docxのためのXMLレイアウトはとても似ていますここ

<Paragraph> 
    <Bookmarkstart name=bookmark1/> 
    <Run> 
     <Text "Item 1"> 
    </Run> 
</Paragraph> 
<Paragraph> 
    <Run> 
     <Text "Item 2"> 
    </Run> 
</Paragraph>  
<Paragraph> 
    <Run> 
     <Text "Item 3"> 
    </Run> 
</Paragraph>  
<Paragraph> 
    <Run> 
     <Text "Item 4"> 
    </Run> 
    <Bookmarkend/> 
</Paragraph>  
とあなたのコード:

  if(bookmarkStart.Name == bookmarkKey) 
      { 
       foreach(Run run in bookmarkStart.Parent.Descendants<Run>()) 
       { 
        returnVal += run.Descendants<Text>().FirstOrDefault().Text + "<br/>"; 
       } 
      } 

bookmarkstart.ParentコールRU NS、それは直接ブックマークを超えているParagraphに一致します。

<Paragraph> 
    <Bookmarkstart name=bookmark1/> 
    <Run> 
     <Text "Item 1"> 
    </Run> 
</Paragraph> 

ループの残りの部分が実行されるときに、あなただけのマージ処理に引き込ま「項目1」を取得します。 BookmarkStartとBookmarkEndの間の4つの段落すべてに対してRunのテキストを正しく一致させるためにロジックを再作成する必要があります。

第二:

bookmarkStart.Parent.Descendants<Run> 

をあなたは正しい、DocumentFormat.OpenXml.Drawing.Runにない参照している場合:あなたが子孫でRunがここに呼んで一致しようとしているときが多いのOpenXmlの人々をアップトリップ別の問題があります'DocumentFormat.OpenXml.Wordprocessing.Run'、Visual StudioのRunにマッチするのを防ぐことができます。使用するステートメントを調整して正しいステートメントを取得します。 A usingステートメントのような

using Run = DocumentFormat.OpenXml.Wordprocessing.Run; 

は、そのファイルの残りのコードに応じてよく使用されます。これらの手がかりがあなたに役立つことを願っています。

+0

これは完全に理にかなっています!私は目が覚めていないので、最初はこれが見えませんでした。だからこそ、私は最初のアイテムしか手に入れませんでした。今は新しいメソッドがあり、SQLからデータを取得しています。そのテキストをフィールドに保存し、 '|||'のような区切り文字を追加しました新しい行のために。正規表現を使用して文字列を分割し、OpenXMLで改行を挿入します。それは非常に醜いですが、それは動作します。私は、複数行のテキストブロックにdocテンプレートを使用するように私のコードをリファクタリングするつもりです。あなたがもっと良い、よりクリーンな方法を知っているなら、私に知らせてください。ありがとうございました! – Cosmos24Magic

関連する問題