简体   繁体   中英

Change font color in OpenXML word document (C#)

I've been searching for hours and I just can't seem to find a solid answer for this. I have an existing document with content controls that I need to edit the text in with external data. If the data for one of the controls is not present, then I need to replace the text with an appropriate notice and change the font color.

I have the text entry and all that working just fine, the only part that won't seem to do its job is changing the font color. The current code I have doesn't give me any errors and is running through this method just fine, but when I look at the finished document its still the plain black text.

My color changing method: (the input is a list of all content controls with the same tag)

public void SetBlueText(List<SdtElement> sdtElement)
{
    foreach (SdtElement element in sdtElement)
    {
        if (element != null)
        {
            RunProperties runProperties = element.Descendants<RunProperties>().FirstOrDefault();
            runProperties.Color = new DocumentFormat.OpenXml.Wordprocessing.Color() { Val = "0EBFE9" };
        }
    }
}

Also, simplifying those two lines down to just this / has the same effect

element.Descendants<RunProperties>().FirstOrDefault().Color = 
                        new DocumentFormat.OpenXml.Wordprocessing.Color() { Val = "0EBFE9" };

I have run into similar issues and discovered that for some reason the order that you append objects to the RunProperties object actually impacts whether or not the formatting update works (The pattern I have noticed is if you append the text prior to doing your formatting, the formatting for that text does not stick).

eg this works (the text becomes bold, Cambria Headings, and the color is set to blue)

Run formattedRun = new Run();
RunProperties runPro = new RunProperties();
RunFonts runFont = new RunFonts() { Ascii = "Cambria(Headings)", HighAnsi = "Cambria(Headings)" };
Bold bold = new Bold();
Text text = new Text("TESTING");
Color color = new Color() { Val = "365F91", ThemeColor = ThemeColorValues.Accent1, ThemeShade = "BF" };
runPro.Append(runFont);
runPro.Append(bold);
runPro.Append(color);
runPro.Append(text);
formattedRun.Append(runPro);

but this does not (The text becomes Cambria Headings and Bold, but the color stays the standard black)

Run formattedRun = new Run();
RunProperties runPro = new RunProperties();
RunFonts runFont = new RunFonts() { Ascii = "Cambria(Headings)", HighAnsi = "Cambria(Headings)" };
Text text = new Text("TESTING");
Bold bold = new Bold();
Color color = new Color() { Val = "365F91", ThemeColor = ThemeColorValues.Accent1, ThemeShade = "BF" };
runPro.Append(runFont);
runPro.Append(bold);
runPro.Append(text);
runPro.Append(color);
formattedRun.Append(runPro);

Well, I kind of brute forced my way to the answer, but it works.

List<RunProperties> runProps = element.Descendants<RunProperties>().ToList();
foreach (RunProperties rp in runProps)
{
    rp.Color = new DocumentFormat.OpenXml.Wordprocessing.Color() { Val = "0EBFE9" };
}

If anyone has a more elegant solution please add it and I'll upvote it.

I had the need for something very similar to the OP in the sense that I had a whole bunch of plain text and other controls in a word document template which I was populating at runtime. I created an extension method to set text and other formatting bits and bobs. Hopefully this will help anyone who comes here in need like I did:

    public static void ReplaceText(this SdtElement element, string replacementText, bool? isBold = null, bool? isItalic = null, System.Drawing.Color? color = null, VerticalPositionValues? textVerticalType = null, int? fontSizeComplexScript = null)
    {

        // First try to get content blocks from the element
        IEnumerable<SdtContentBlock> childContentBlocks = element.ChildElements.OfType<SdtContentBlock>();

        //Function to generate the new run properties
        RunProperties SetupNewRunProperties(RunProperties oldRunProps) { 

            var props = new RunProperties();

            if (fontSizeComplexScript.HasValue && fontSizeComplexScript.HasValue)
                props.FontSizeComplexScript.Val = fontSizeComplexScript.ToString();
            else if (oldRunProps?.FontSizeComplexScript != null)
                props.FontSizeComplexScript = (FontSizeComplexScript)oldRunProps.FontSizeComplexScript.CloneNode(true);

            if (isBold.HasValue) 
                props.Bold.Val = OnOffValue.FromBoolean(isBold.Value);
            else if(oldRunProps?.Bold != null)
                props.Bold = (Bold)oldRunProps.Bold.CloneNode(true);

            if (isItalic.HasValue)
                props.Italic.Val = OnOffValue.FromBoolean(isItalic.Value);
            else if (oldRunProps?.Italic != null)
                props.Italic = (Italic)oldRunProps.Italic.CloneNode(true);

            if (textVerticalType.HasValue)
                props.VerticalTextAlignment.Val = textVerticalType.Value;
            else if (oldRunProps?.VerticalTextAlignment != null)
                props.VerticalTextAlignment = (VerticalTextAlignment)oldRunProps.VerticalTextAlignment.CloneNode(true);

            if (color.HasValue)
            {
                if (props.Color != null)
                    props.Color.Val = color.Value.ToHexString();
                else
                    props.Color = new Color() { Val = color.Value.ToHexString() };
            }
            else if (oldRunProps?.Color != null)
            {
                props.Color = (Color)oldRunProps?.Color.CloneNode(true);
            }

            return props;
        }

        if (childContentBlocks.Count() > 0)
        {
            SdtContentBlock contentBlock = childContentBlocks.First();
            Paragraph para = contentBlock.ChildElements.OfType<Paragraph>().First();
            if (para != null)
            {
                Run run = para.GetFirstChild<Run>();

                run.RunProperties = SetupNewRunProperties(run.RunProperties);

                Text text = run.GetFirstChild<Text>();
                text.Text = replacementText;
            }
        }
        else
        {
            // Instead, try to get content runs from the element
            IEnumerable<SdtContentRun> childContentRuns = element.ChildElements.OfType<SdtContentRun>();

            if (childContentRuns.Count() > 0)
            {
                Run run = childContentRuns.First().GetFirstChild<Run>();

                run.RunProperties = SetupNewRunProperties(run.RunProperties);

                Text text = run.GetFirstChild<Text>();
                text.Text = replacementText;
            }
        }

    }

Color val should be 8 digits. For example Color.Val="FFFF0000" display string in red.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM