使用MS OpenXml Sdk我已经能够将模板流复制到结果流,并使用以下代码在正文末尾添加动态文本(w.p>> w.r>> wt): / p>
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中文本框的innertext替换为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将附加到正文的末尾。在第二个示例中,虽然文本框及其内容保持不变,即使调试器中的进一步检查显示textboxContent的子项反映了上面所做的更改。
我是OpenXML开发的新手,所以如果有任何明显的东西请指出。
答案 0 :(得分:4)
当你在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/
在我的情况下,我只是修改了后备文本框(v.txbx而不是wps.txbx),因为我假设Resharper正确地要求我导入DocumentFormat.OpenXml.Vml命名空间以依赖TextBox对象。不确定为什么在我已经包含的名称空间之一,DocumentFormat.OpenXml.Packaging或DocumentFormat.OpenXml.Wordprocessing中没有Textbox定义,但这超出了这个问题的范围。毋庸置疑,在实现这一点并更新我的代码以寻找两个常见的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:)