如何将内容放在docx的合并域中

我正在使用asp.net开发一个Web应用程序,我有一个名为Template.docx的文件,它像模板一样生成其他报告。 在这个Template.docx中,我有一些MergeFields(Title,CustomerName,Content,Footer等)来代替C#中的一些动态内容。

我想知道,如何将内容放在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转换为文件时,我得到了一个损坏的文件。 谢谢!

您可以尝试http://www.codeproject.com/KB/office/Fill_Mergefields.aspx ,它使用Open XML SDK来执行此操作。

我知道这是一个老post,但我无法得到为我工作的公认答案。 链接的项目甚至不会编译(有人已在该链接中评论过)。 此外,它似乎使用其他Nuget包,如WPFToolkit。

所以我在这里添加我的答案,以防有人发现它有用。 这仅使用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 (eg, .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(); var mergeFieldName = "SenderFullName"; var replacementText = "John Smith"; ReplaceMergeFieldWithText(mergeFields, mergeFieldName, replacementText); // Save the document mainPart.Document.Save(); } private void ReplaceMergeFieldWithText(IEnumerable 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 rSep = rFldCode.NextSibling(); Run rText = rSep.NextSibling(); Run rEnd = rText.NextSibling(); // 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(); t.Text = replacementText; // Remove all the four (4) Runs for our merge field rFldCode.Remove(); rBegin.Remove(); rSep.Remove(); rEnd.Remove(); } } 

上面的代码基本上是这样的:

  • 确定构成名为“SenderFullName”的合并字段的4个运行。
  • 确定包含合并字段的Text元素的Run。
  • 删除4次运行。
  • 更新合并字段的Text元素的text属性。

UPDATE

对于任何感兴趣的人, 这是一个简单的静态类,我曾经帮助我替换合并字段。

Frank Fajardo的回答是我的99%,但重要的是要注意MERGEFIELDS可以是SimpleFields或FieldCodes。

对于SimpleFields,在文档中向用户显示的文本运行是SimpleField的子项。

在FieldCodes的情况下,显示给用户的文本运行在包含具有Separate和End FieldCharValues的FieldChars的运行之间。 有时,在Separate和End Elements之间存在多个包含运行的文本。

下面的代码处理这些问题。 有关如何从文档中获取所有MERGEFIELDS的更多详细信息,包括页眉和页脚,请访问GitHub存储库, url为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(); 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 GetAssociatedRuns(FieldCode fieldCode) { Run rFieldCode = (Run)fieldCode.Parent; Run rBegin = rFieldCode.PreviousSibling(); Run rCurrent = rFieldCode.NextSibling(); var runs = new List(new[] { rBegin, rCurrent }); while (!rCurrent.ContainsCharType(FieldCharValues.End)) { rCurrent = rCurrent.NextSibling(); runs.Add(rCurrent); }; return runs; } private static bool ContainsCharType(this Run run, FieldCharValues fieldCharType) { var fc = run.GetFirstChild(); return fc == null ? false : fc.FieldCharType.Value == fieldCharType; }