2
votes

By the help of some very kind community members here I managed to programatically create a function to replace text inside content controls in a Word document using open xml. After the document is generated it removes the formatting of the text after I replace the text.

Any ideas on how I can still keep the formatting in word and remove the content control tags ?

This is my code:

using (var wordDoc = WordprocessingDocument.Open(mem, true))
{

    var mainPart = wordDoc.MainDocumentPart;

    ReplaceTags(mainPart, "FirstName", _firstName);
    ReplaceTags(mainPart, "LastName", _lastName);
    ReplaceTags(mainPart, "WorkPhoe", _workPhone);
    ReplaceTags(mainPart, "JobTitle", _jobTitle);

    mainPart.Document.Save();
    SaveFile(mem);
}

private static void ReplaceTags(MainDocumentPart mainPart, string tagName,    string tagValue)
{
    //grab all the tag fields
    IEnumerable<SdtBlock> tagFields = mainPart.Document.Body.Descendants<SdtBlock>().Where
        (r => r.SdtProperties.GetFirstChild<Tag>().Val == tagName);

    foreach (var field in tagFields)
    {
        //remove all paragraphs from the content block
        field.SdtContentBlock.RemoveAllChildren<Paragraph>();
        //create a new paragraph containing a run and a text element
        Paragraph newParagraph = new Paragraph();
        Run newRun = new Run();
        Text newText = new Text(tagValue);
        newRun.Append(newText);
        newParagraph.Append(newRun);
        //add the new paragraph to the content block
        field.SdtContentBlock.Append(newParagraph);
    }
}
1

1 Answers

1
votes

Keeping the style is a tricky problem as there could be more than one style applied to the text you are trying to replace. What should you do in that scenario?

Assuming a simple case of one style (but potentially over many Paragraphs, Runs and Texts) you could keep the first Text element you come across per SdtBlock and place your required value in that element then delete any further Text elements from the SdtBlock. The formatting from the first Text element will then be maintained. Obviously you can apply this theory to any of the Text blocks; you don't have to necessarily use the first. The following code should show what I mean:

private static void ReplaceTags(MainDocumentPart mainPart, string tagName, string tagValue)
{
    IEnumerable<SdtBlock> tagFields = mainPart.Document.Body.Descendants<SdtBlock>().Where
        (r => r.SdtProperties.GetFirstChild<Tag>().Val == tagName);

    foreach (var field in tagFields)
    {
        IEnumerable<Text> texts = field.SdtContentBlock.Descendants<Text>();

        for (int i = 0; i < texts.Count(); i++)
        {
            Text text = texts.ElementAt(i);

            if (i == 0)
            {
                text.Text = tagValue;
            }
            else
            {
                text.Remove();
            }
        }
    }
}