2011-12-06 10 views
3

私はasp.netでWebアプリケーションを開発していて、他のレポートを生成するテンプレートのように動作するTemplate.docxというファイルがあります。このTemplate.docxの内部には、C#のいくつかの動的コンテンツを置き換えるいくつかのMergeField(Title、CustomerName、Content、Footerなど)があります。どのように私は、docxのmergefieldにコンテンツを配置することができます

私は、どのようにしてdocxのマージフィールドにコンテンツを置くことができますか?

MergeFieldsがこれを行う正しい方法か、別の方法があるかどうかはわかりません。あなたが私に示唆することができれば、感謝します!

PS:Webアプリケーションでopenxmlが参照されています。

編集:

private MemoryStream LoadFileIntoStream(string fileName) 
{ 
    MemoryStream memoryStream = new MemoryStream(); 
    using (FileStream fileStream = File.OpenRead(fileName)) 
    { 
     memoryStream.SetLength(fileStream.Length); 
     fileStream.Read(memoryStream.GetBuffer(), 0, (int) fileStream.Length); 

     memoryStream.Flush(); 
     fileStream.Close(); 
    } 
    return memoryStream; 
} 

public MemoryStream GenerateWord() 
{ 
    string templateDoc = "C:\\temp\\template.docx"; 
    string reportFileName = "C:\\temp\\result.docx"; 

    var reportStream = LoadFileIntoStream(templateDoc); 

    // Copy a new file name from template file 
    //File.Copy(templateDoc, reportFileName, true); 

    // Open the new Package 
    Package pkg = Package.Open(reportStream, FileMode.Open, FileAccess.ReadWrite); 

    // Specify the URI of the part to be read 
    Uri uri = new Uri("/word/document.xml", UriKind.Relative); 
    PackagePart part = pkg.GetPart(uri); 

    XmlDocument xmlMainXMLDoc = new XmlDocument(); 
    xmlMainXMLDoc.Load(part.GetStream(FileMode.Open, FileAccess.Read)); 

    // replace some keys inside xml (it will come from database, it's just a test) 
    xmlMainXMLDoc.InnerXml = xmlMainXMLDoc.InnerXml.Replace("field_customer", "My Customer Name"); 
    xmlMainXMLDoc.InnerXml = xmlMainXMLDoc.InnerXml.Replace("field_title", "Report of Documents"); 
    xmlMainXMLDoc.InnerXml = xmlMainXMLDoc.InnerXml.Replace("field_content", "Content of Document"); 

    // Open the stream to write document 
    StreamWriter partWrt = new StreamWriter(part.GetStream(FileMode.Open, FileAccess.Write)); 
    //doc.Save(partWrt); 
    xmlMainXMLDoc.Save(partWrt); 

    partWrt.Flush(); 
    partWrt.Close(); 
    reportStream.Flush(); 
    pkg.Close(); 

    return reportStream; 
} 

PS:私は、ファイルへのMemoryStreamを変換すると、私は破損したファイルを得ました。ありがとう!

+0

でGitHubのリポジトリで利用可能である私は、差し込み印刷をperfromするWordを使用すると、ここにオプションではないことを前提としていますか? – user957902

+0

いいえ、実際には、これらのフィールドに入力する必要があります。動的コンテンツに置き換えます。 MergeFieldsがこれを行う正しい方法であるかどうかはわかりません。ありがとうございました! –

+0

通常、MergeFieldにコンテンツを挿入する方法は、動的コンテンツを含むデータソースとの差し込み印刷を実行することです。あなたはasp.netコードからdocxファイルを操作するためにlibarayを使用していますか? – user957902

答えて

1

私はこれが古い投稿であることを知っていますが、私は受け入れられた答えを私のために働かせることができませんでした。リンクされたプロジェクトはコンパイルされません(誰かが既にそのリンクにコメントしています)。また、WPFToolkitのような他のNugetパッケージを使用しているようです。

だから誰かが役に立ったと思ったら、私の答えをここに追加します。これは、OpenXML SDK 2.5とWindowsBase v4のみを使用します。これはMS Word 2010以降で動作します。上記のコードは何

string sourceFile = @"C:\Template.docx"; 
string targetFile = @"C:\Result.docx"; 
File.Copy(sourceFile, targetFile, true); 
using (WordprocessingDocument document = WordprocessingDocument.Open(targetFile, true)) 
{ 
    // If your sourceFile is a different type (e.g., .DOTX), you will need to change the target type like so: 
    document.ChangeDocumentType(WordprocessingDocumentType.Document); 

    // Get the MainPart of the document 
    MainDocumentPart mainPart = document.MainDocumentPart; 
    var mergeFields = mainPart.RootElement.Descendants<FieldCode>(); 

    var mergeFieldName = "SenderFullName"; 
    var replacementText = "John Smith"; 

    ReplaceMergeFieldWithText(mergeFields, mergeFieldName, replacementText);     

    // Save the document 
    mainPart.Document.Save(); 

} 


private void ReplaceMergeFieldWithText(IEnumerable<FieldCode> fields, string mergeFieldName, string replacementText) 
{ 
    var field = fields 
     .Where(f => f.InnerText.Contains(mergeFieldName)) 
     .FirstOrDefault(); 

    if (field != null) 
    { 
     // Get the Run that contains our FieldCode 
     // Then get the parent container of this Run 
     Run rFldCode = (Run)field.Parent; 

     // Get the three (3) other Runs that make up our merge field 
     Run rBegin = rFldCode.PreviousSibling<Run>(); 
     Run rSep = rFldCode.NextSibling<Run>(); 
     Run rText = rSep.NextSibling<Run>(); 
     Run rEnd = rText.NextSibling<Run>(); 

     // Get the Run that holds the Text element for our merge field 
     // Get the Text element and replace the text content 
     Text t = rText.GetFirstChild<Text>(); 
     t.Text = replacementText; 

     // Remove all the four (4) Runs for our merge field 
     rFldCode.Remove(); 
     rBegin.Remove(); 
     rSep.Remove(); 
     rEnd.Remove(); 
    } 

} 

は基本的にはこれです:

  • は「SenderFullName」という名前のマージフィールドを構成する4ランを識別します。
  • [マージ]フィールドの[テキスト]要素を含む実行を特定します。
  • 4回の実行を削除します。
  • マージフィールドのText要素のtextプロパティを更新します。興味がある人々のために

UPDATE

hereは、私がフィールドをマージ交換で私を助けるために使用される単純な静的なクラスです。

+0

ありがとうございます@フランクファハルド。マイナーな変更はmakeのように行います - 1)varコンテナは決して使用されず、宣言はコードから削除できます。 2)rTextの宣言はrSepとrEndの間になければなりません(そうでなければ宣言されていない変数を使用します)。 – Brent

+0

@Brent、そうです。私はコピーアンドペーストして、それをはっきりさせるように修正しましたが、正しくはしませんでした。ありがとう! –

+0

@Brent、興味がある場合に備えて、私が最終的に使用したコードへのリンクを追加しました。 –

1

フランクファハルドの回答は私のために99%ありましたが、MERGEFIELDSはSimpleFieldsまたはFieldCodesであることに注意することが重要です。

SimpleFieldsの場合、ドキュメントのユーザーに表示されるテキストランは、SimpleFieldの子です。

FieldCodesの場合、ユーザーに表示されるテキストの実行は、FieldCharsとSeparateとEnd FieldCharValuesを含む実行の間です。場合によっては、分離要素と終了要素の間に複数のテキストが含まれています。

次のコードは、これらの問題を扱っています。ヘッダーとフッターを含む文書からすべてのMERGEFIELDSを取得する方法の詳細は、https://github.com/mcshaz/SimPlanner/blob/master/SP.DTOs/Utilities/OpenXmlExtensions.cs

private static Run CreateSimpleTextRun(string text) 
{ 
    Run returnVar = new Run(); 
    RunProperties runProp = new RunProperties(); 
    runProp.Append(new NoProof()); 
    returnVar.Append(runProp); 
    returnVar.Append(new Text() { Text = text }); 
    return returnVar; 
} 

private static void InsertMergeFieldText(OpenXmlElement field, string replacementText) 
{ 
    var sf = field as SimpleField; 
    if (sf != null) 
    { 
     var textChildren = sf.Descendants<Text>(); 
     textChildren.First().Text = replacementText; 
     foreach (var others in textChildren.Skip(1)) 
     { 
      others.Remove(); 
     } 
    } 
    else 
    { 
     var runs = GetAssociatedRuns((FieldCode)field); 
     var rEnd = runs[runs.Count - 1]; 
     foreach (var r in runs 
      .SkipWhile(r => !r.ContainsCharType(FieldCharValues.Separate)) 
      .Skip(1) 
      .TakeWhile(r=>r!= rEnd)) 
     { 
      r.Remove(); 
     } 
     rEnd.InsertBeforeSelf(CreateSimpleTextRun(replacementText)); 
    } 
} 

private static IList<Run> GetAssociatedRuns(FieldCode fieldCode) 
{ 
    Run rFieldCode = (Run)fieldCode.Parent; 
    Run rBegin = rFieldCode.PreviousSibling<Run>(); 
    Run rCurrent = rFieldCode.NextSibling<Run>(); 

    var runs = new List<Run>(new[] { rBegin, rCurrent }); 

    while (!rCurrent.ContainsCharType(FieldCharValues.End)) 
    { 
     rCurrent = rCurrent.NextSibling<Run>(); 
     runs.Add(rCurrent); 
    }; 

    return runs; 
} 

private static bool ContainsCharType(this Run run, FieldCharValues fieldCharType) 
{ 
    var fc = run.GetFirstChild<FieldChar>(); 
    return fc == null 
     ? false 
     : fc.FieldCharType.Value == fieldCharType; 
} 
関連する問題