Замена внутреннего текста текстового поля


С помощью 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 2

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:)