How to keep style on open xml documents
Asked Answered
S

1

5

I am using open XML(Microsoft Word - .docx) as a file template to automatically generate other documents. In the template document I have defined content controls, and I have written code to replace content in these content controls.

The content is replaced and the documents are generated, but I am struggling with keeping the style. In Word, when inspecting properties of the content control, I have checked the checbox for "Use a style to format text into the empty control: style", and also checked for "Remove content controls when content are edited". This doesn't seem to have any impact when documents are generated by code.

Here is how I set the properties of the content control in Word This is my code (which a community member here was kind enough to help with) for replacing the data in the content controls. Any ideas what I should do in order to keep the formatting? The formatting is simple text formatting, like size and font. Please advice:

    private static void ReplaceTags(MainDocumentPart mainPart, string tagName, string tagValue)
    {
        //grab all the tag fields
        var 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<DocumentFormat.OpenXml.Wordprocessing.Paragraph>();
            //create a new paragraph containing a run and a text element
            var newParagraph = new DocumentFormat.OpenXml.Wordprocessing.Paragraph();
            var newRun = new DocumentFormat.OpenXml.Wordprocessing.Run();
            var newText = new DocumentFormat.OpenXml.Wordprocessing.Text(tagValue);
            newRun.Append(newText);
            newParagraph.Append(newRun);
            //add the new paragraph to the content block
            field.SdtContentBlock.Append(newParagraph);
        }
    }
Sigurd answered 17/3, 2015 at 6:43 Comment(0)
B
9

When you assign a style to the content control a new RunProperties element is added under the SdtProperties. For example, if I assign a new style called Style1 I can see the following XML is generated:

<w:sdt>
    <w:sdtPr>
        <w:rPr>
            <w:rStyle w:val="Style1" />
        </w:rPr>
    <w:alias w:val="LastName" />
    <w:tag w:val="LastName" />
    ....

You need to grab this value and assign it to the new Paragraph you are creating, add the Paragraph at the same level as the SdtBlock and then remove the SdtBlock which is what Word does when you select the "Remove content control when contents are edited" option. The RunProperties are the <w:rPr> element. The following should do what you're after.

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)
    {
        //grab the RunProperties from the SdtBlcok
        RunProperties runProp = field.SdtProperties.GetFirstChild<RunProperties>();

        //create a new paragraph containing a run and a text element
        Paragraph newParagraph = new Paragraph();
        Run newRun = new Run();
        if (runProp != null)
        {
            //assign the RunProperties to our new run
            newRun.Append(runProp.CloneNode(true));
        }
        Text newText = new Text(tagValue);
        newRun.Append(newText);
        newParagraph.Append(newRun);
        //insert the new paragraph before the field we're going to remove
        field.Parent.InsertBefore(newParagraph, field);

        //remove the SdtBlock to mimic the Remove content control when contents are edited option
        field.Remove();
    }
}
Brnaby answered 17/3, 2015 at 12:29 Comment(3)
Hi again, and thanks a million for your awsome coding help and very good explanation. The code worked with regards to keeping style, but I got a new problem along the way :). When running this code, it keeps the styling intact, but after input of new text inside the content control, the text is placed at the bottom of the page. I had it on top. Any ideas how to make it keep it's position `? Thanks againSigurd
Thanks @Ilyas. The line field.Parent.AppendChild(newParagraph); was adding the new paragraph at the end of the document. I've edited the answer to use field.Parent.InsertBefore(newParagraph, field); which will add it before the content control (which then gets deleted, so essentially it's at the same point as the content control was)Brnaby
Excellent piece of code! You can even use this to keep the same format when creating multiple lines within the same paragraph, by calling the following within a loop: RunProperties runProps = newRun.AppendChild(new RunProperties()); Break linebreak = new Break(); runProps.Append(linebreak);Lover

© 2022 - 2024 — McMap. All rights reserved.