Замена внутреннего текста текстового поля
С помощью MS OpenXml Sdk я смог скопировать templatestream в resultstream и добавить динамический текст (w. p>>w. r>>w. t) в конце тела, используя следующий код:
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();
}
Мой следующий логический шаг состоял в том, чтобы заменить внутренний текст текстового поля в результирующем потоке на имя 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, а первое имя добавляется в конец тело, если смотреть на него в слове. Во втором примере текстовое поле и его содержимое остаются неизменными, хотя дальнейшее изучение в отладчике показало, что дочерние элементы textboxContent отражают внесенные выше изменения.
Я новичок в разработке OpenXML, поэтому, если есть что-то очевидное, пожалуйста, укажите на это.
1 ответ:
Ого, я даже не хочу думать, что понимаю всю картину вокруг этого, но вот быстрый удар по ней. Кто-то с большим опытом openXml, пожалуйста, звоните...
Получается, когда вы создаете текстовое поле в word на docx документе.xml-файл get имеет следующую разметку:
<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.Выбор, и mc.Резервные метки. Что это за чертовщина??? Кто-то так выразился в статье блога, на которую я наткнулся -
" Как я понимаю - но не верьте мне на слово, как Евангелие-AlternateContent может появляется в любом месте и обеспечивает механизм включения расширенного функциональность, если потребляющее приложение может справиться с ней, наряду с запасной вариант, если не получится." - Тони Джолланс ... http://social.msdn.microsoft.com/Forums/en-US/worddev/thread/f8a5c277-7049-48c2-a295-199d2914f4ba/
В моем случае я только модифицировал резервное текстовое поле (v. txbx не wps.txbx) из-за моей неудачи в предположении, что Решарпер был прав, попросив меня импортировать пространстве имен documentformat.функция OPENXML.Пространство имен Vml для моей зависимости от объекта TextBox. Не знаю, почему в одном из моих уже включенных пространств имен DocumentFormat нет определения текстового поля.функция OPENXML.Упаковка или имен documentformat.функция OPENXML.Обработка текстов, но это выходит за рамки данного вопроса. Излишне говорить, что, осознав это и обновив свой код, чтобы найти общий w.txbxContent для этих двух, я достиг того, что хотел сделать.
Вот обновленный код с некоторым рефакторингом, вызовите Метод ReplaceTag в операторе using из исходного вопроса и предоставить объект модели вместо строки. Кроме того, для удобства используйте словарь 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:)