Apply a style to a paragraph in a word processing document
This topic shows how to use the classes in the Open XML SDK for Office to programmatically apply a style to a paragraph within a word processing document. It contains an example ApplyStyleToParagraph method to illustrate this task, plus several supplemental example methods to check whether a style exists, add a new style, and add the styles part.
ApplyStyleToParagraph method
The ApplyStyleToParagraph example method can be used to apply a style to a paragraph. You must first obtain a reference to the document as well as a reference to the paragraph that you want to style. The method accepts four parameters that indicate: the reference to the opened word processing document, the styleid of the style to be applied, the name of the style to be applied, and the reference to the paragraph to which to apply the style.
public static void ApplyStyleToParagraph(WordprocessingDocument doc, string styleid, string stylename, Paragraph p)
The following sections in this topic explain the implementation of this method and the supporting code, as well as how to call it. The complete sample code listing can be found in the Sample Code section at the end of this topic.
Get a WordprocessingDocument object
The Sample Code section also shows the code required to set up for calling the sample method. To use the method to apply a style to a paragraph in a document, you first need a reference to the open document. In the Open XML SDK, the WordprocessingDocument class represents a Word document package. To open and work with a Word document, create an instance of the WordprocessingDocument class from the document. After you create the instance, use it to obtain access to the main document part that contains the text of the document. The content in the main document part is represented in the package as XML using WordprocessingML markup.
To create the class instance, call one of the overloads of the Open() method. The following sample code shows how to use the WordprocessingDocument.Open overload. The first parameter takes a string that represents the full path to the document to open. The second parameter takes a value of true or false and represents whether to open the file for editing. In this example the parameter is true to enable read/write access to the file.
using (WordprocessingDocument doc =
WordprocessingDocument.Open(fileName, true))
{
// Code removed here.
}
Structure of a WordProcessingML Document
The basic document structure of a WordProcessingML document consists of the document and body elements, followed by one or more block level elements such as p, which represents a paragraph. A paragraph contains one or more r elements. The r stands for run, which is a region of text with a common set of properties, such as formatting. A run contains one or more t elements. The t element contains a range of text. The following code example shows the WordprocessingML markup for a document that contains the text "Example text."
<w:document xmlns:w="https://schemas.openxmlformats.org/wordprocessingml/2006/main">
<w:body>
<w:p>
<w:r>
<w:t>Example text.</w:t>
</w:r>
</w:p>
</w:body>
</w:document>
Using the Open XML SDK, you can create document structure and content using strongly-typed classes that correspond to WordprocessingML elements. You will find these classes in the DocumentFormat.OpenXml.Wordprocessing namespace. The following table lists the class names of the classes that correspond to the document, body, p, r, and t elements.
WordprocessingML Element | Open XML SDK Class | Description |
---|---|---|
document | Document | The root element for the main document part. |
body | Body | The container for the block level structures such as paragraphs, tables, annotations and others specified in the ISO/IEC 29500 specification. |
p | Paragraph | A paragraph. |
r | Run | A run. |
t | Text | A range of text. |
For more information about the overall structure of the parts and elements of a WordprocessingML document, see Structure of a WordprocessingML document.
Get the paragraph to style
After opening the file, the sample code retrieves a reference to the first paragraph. Because a typical word processing document body contains many types of elements, the code filters the descendants in the body of the document to those of type Paragraph. The ElementAtOrDefault method is then employed to retrieve a reference to the paragraph. Because the elements are indexed starting at zero, you pass a zero to retrieve the reference to the first paragraph, as shown in the following code example.
// Get the first paragraph.
Paragraph p =
doc.MainDocumentPart.Document.Body.Descendants<Paragraph>()
.ElementAtOrDefault(0);
// Check for a null reference.
if (p == null)
{
// Code removed here…
}
The reference to the found paragraph is stored in a variable named p. If a paragraph is not found at the specified index, the ElementAtOrDefault method returns null as the default value. This provides an opportunity to test for null and throw an error with an appropriate error message.
Once you have the references to the document and the paragraph, you can call the ApplyStyleToParagraph example method to do the remaining work. To call the method, you pass the reference to the document as the first parameter, the styleid of the style to apply as the second parameter, the name of the style as the third parameter, and the reference to the paragraph to which to apply the style, as the fourth parameter.
Add the paragraph properties element
The first step of the example method is to ensure that the paragraph has a paragraph properties element. The paragraph properties element is a child element of the paragraph and includes a set of properties that allow you to specify the formatting for the paragraph.
The following information from the ISO/IEC 29500 specification introduces the pPr (paragraph properties) element used for specifying the formatting of a paragraph. Note that section numbers preceded by § are from the ISO specification.
Within the paragraph, all rich formatting at the paragraph level is stored within the pPr element (§17.3.1.25; §17.3.1.26). [Note: Some examples of paragraph properties are alignment, border, hyphenation override, indentation, line spacing, shading, text direction, and widow/orphan control.
Among the properties is the pStyle element to specify the style to apply to the paragraph. For example, the following sample markup shows a pStyle element that specifies the "OverdueAmount" style.
<w:p xmlns:w="https://schemas.openxmlformats.org/wordprocessingml/2006/main">
<w:pPr>
<w:pStyle w:val="OverdueAmount" />
</w:pPr>
...
</w:p>
In the Open XML SDK, the pPr element is represented by the ParagraphProperties class. The code determines if the element exists, and creates a new instance of the ParagraphProperties class if it does not. The pPr element is a child of the p (paragraph) element; consequently, the PrependChild<T>(T) method is used to add the instance, as shown in the following code example.
// If the paragraph has no ParagraphProperties object, create one.
if (p.Elements<ParagraphProperties>().Count() == 0)
{
p.PrependChild<ParagraphProperties>(new ParagraphProperties());
}
// Get the paragraph properties element of the paragraph.
ParagraphProperties pPr = p.Elements<ParagraphProperties>().First();
Add the Styles part
With the paragraph found and the paragraph properties element present, now ensure that the prerequisites are in place for applying the style. Styles in WordprocessingML are stored in their own unique part. Even though it is typically true that the part as well as a set of base styles are created automatically when you create the document by using an application like Microsoft Word, the styles part is not required for a document to be considered valid. If you create the document programmatically using the Open XML SDK, the styles part is not created automatically; you must explicitly create it. Consequently, the following code verifies that the styles part exists, and creates it if it does not.
// Get the Styles part for this document.
StyleDefinitionsPart part =
doc.MainDocumentPart.StyleDefinitionsPart;
// If the Styles part does not exist, add it and then add the style.
if (part == null)
{
part = AddStylesPartToPackage(doc);
// Code removed here...
}
The AddStylesPartToPackage example method does the work of adding the styles part. It creates a part of the StyleDefinitionsPart type, adding it as a child to the main document part. The code then appends the Styles root element, which is the parent element that contains all of the styles. The Styles element is represented by the Styles class in the Open XML SDK. Finally, the code saves the part.
// Add a StylesDefinitionsPart to the document. Returns a reference to it.
public static StyleDefinitionsPart AddStylesPartToPackage(WordprocessingDocument doc)
{
StyleDefinitionsPart part;
part = doc.MainDocumentPart.AddNewPart<StyleDefinitionsPart>();
Styles root = new Styles();
root.Save(part);
return part;
}
Verify that the style exists
Applying a style that does not exist to a paragraph has no effect; there is no exception generated and no formatting changes occur. The example code verifies the style exists prior to attempting to apply the style. Styles are stored in the styles part, therefore if the styles part does not exist, the style itself cannot exist.
If the styles part exists, the code verifies a matching style by calling the IsStyleIdInDocument example method and passing the document and the styleid. If no match is found on styleid, the code then tries to lookup the styleid by calling the GetStyleIdFromStyleName example method and passing it the style name.
If the style does not exist, either because the styles part did not exist, or because the styles part exists, but the style does not, the code calls the AddNewStyle example method to add the style.
// Get the Styles part for this document.
StyleDefinitionsPart part =
doc.MainDocumentPart.StyleDefinitionsPart;
// If the Styles part does not exist, add it and then add the style.
if (part == null)
{
part = AddStylesPartToPackage(doc);
AddNewStyle(part, styleid, stylename);
}
else
{
// If the style is not in the document, add it.
if (IsStyleIdInDocument(doc, styleid) != true)
{
// No match on styleid, so let's try style name.
string styleidFromName = GetStyleIdFromStyleName(doc, stylename);
if (styleidFromName == null)
{
AddNewStyle(part, styleid, stylename);
}
else
styleid = styleidFromName;
}
}
Within the IsStyleInDocument example method, the work begins with retrieving the Styles element through the Styles property of the StyleDefinitionsPart of the main document part, and then determining whether any styles exist as children of that element. All style elements are stored as children of the styles element.
If styles do exist, the code looks for a match on the styleid. The styleid is an attribute of the style that is used in many places in the document to refer to the style, and can be thought of as its primary identifier. Typically you use the styleid to identify a style in code. The FirstOrDefault method defaults to null if no match is found, so the code verifies for null to see whether a style was matched, as shown in the following excerpt.
// Return true if the style id is in the document, false otherwise.
public static bool IsStyleIdInDocument(WordprocessingDocument doc,
string styleid)
{
// Get access to the Styles element for this document.
Styles s = doc.MainDocumentPart.StyleDefinitionsPart.Styles;
// Check that there are styles and how many.
int n = s.Elements<Style>().Count();
if (n==0)
return false;
// Look for a match on styleid.
Style style = s.Elements<Style>()
.Where(st => (st.StyleId == styleid) && (st.Type == StyleValues.Paragraph))
.FirstOrDefault();
if (style == null)
return false;
return true;
}
When the style cannot be found based on the styleid, the code attempts to find a match based on the style name instead. The GetStyleIdFromStyleName example method does this work, looking for a match on style name and returning the styleid for the matching element if found, or null if not.
Add the style to the styles part
The AddNewStyle example method takes three parameters. The first parameter takes a reference to the styles part. The second parameter takes the styleid of the style, and the third parameter takes the style name. The AddNewStyle code creates the named style definition within the specified part.
To create the style, the code instantiates the Style class and sets certain properties, such as the Type of style (paragraph) and StyleId. As mentioned above, the styleid is used by the document to refer to the style, and can be thought of as its primary identifier. Typically you use the styleid to identify a style in code. A style can also have a separate user friendly style name to be shown in the user interface. Often the style name therefore appears in proper case and with spacing (for example, Heading 1), while the styleid is more succinct (for example, heading1) and intended for internal use. In the following sample code, the styleid and style name take their values from the styleid and stylename parameters.
The next step is to specify a few additional properties, such as the style upon which the new style is based, and the style to be automatically applied to the next paragraph. The code specifies both of these as the "Normal" style. Be aware that the value to specify here is the styleid for the normal style. The code appends these properties as children of the style element.
Once the code has finished instantiating the style and setting up the basic properties, now work on the style formatting. Style formatting is performed in the paragraph properties (pPr) and run properties (rPr) elements. To set the font and color characteristics for the runs in a paragraph, you use the run properties.
To create the rPr element with the appropriate child elements, the code creates an instance of the StyleRunProperties class and then appends instances of the appropriate property classes. For this code example, the style specifies the Lucida Console font, a point size of 12, rendered in bold and italic, using the Accent2 color from the document theme part. The point size is specified in half-points, so a value of 24 is equivalent to 12 point.
When the style definition is complete, the code appends the style to the styles element in the styles part, as shown in the following code example.
// Create a new style with the specified styleid and stylename and add it to the specified
// style definitions part.
private static void AddNewStyle(StyleDefinitionsPart styleDefinitionsPart,
string styleid, string stylename)
{
// Get access to the root element of the styles part.
Styles styles = styleDefinitionsPart.Styles;
// Create a new paragraph style and specify some of the properties.
Style style = new Style() { Type = StyleValues.Paragraph,
StyleId = styleid,
CustomStyle = true };
StyleName styleName1 = new StyleName() { Val = stylename };
BasedOn basedOn1 = new BasedOn() { Val = "Normal" };
NextParagraphStyle nextParagraphStyle1 = new NextParagraphStyle() { Val = "Normal" };
style.Append(styleName1);
style.Append(basedOn1);
style.Append(nextParagraphStyle1);
// Create the StyleRunProperties object and specify some of the run properties.
StyleRunProperties styleRunProperties1 = new StyleRunProperties();
Bold bold1 = new Bold();
Color color1 = new Color() { ThemeColor = ThemeColorValues.Accent2 };
RunFonts font1 = new RunFonts() { Ascii = "Lucida Console" };
Italic italic1 = new Italic();
// Specify a 12 point size.
FontSize fontSize1 = new FontSize() { Val = "24" };
styleRunProperties1.Append(bold1);
styleRunProperties1.Append(color1);
styleRunProperties1.Append(font1);
styleRunProperties1.Append(fontSize1);
styleRunProperties1.Append(italic1);
// Add the run properties to the style.
style.Append(styleRunProperties1);
// Add the style to the styles part.
styles.Append(style);
}
Apply the style to the paragraph
Now, the example code has located the paragraph, added the required paragraph properties element if required, checked for the styles part and added it if required, and checked for the style and added it if required. Now, set the paragraph style. To accomplish this task, the code creates an instance of the ParagraphStyleId class with the styleid and then places a reference to that instance in the ParagraphStyleId property of the paragraph properties object. This creates and assigns the appropriate value to the pStyle element that specifies the style to use for the paragraph.
// Set the style of the paragraph.
pPr.ParagraphStyleId = new ParagraphStyleId(){Val = styleid};
Sample code
You can use the ApplyStyleToParagraph example method to apply a named style to a paragraph in a word processing document using the Open XML SDK. The following code shows how to open and acquire a reference to a word processing document, retrieve a reference to the first paragraph, and then call the ApplyStyleToParagraph method.
To call the method, pass the reference to the document as the first parameter, the styleid of the style to apply as the second parameter, the name of the style as the third parameter, and the reference to the paragraph to which to apply the style, as the fourth parameter. For example, the following code applies the "Overdue Amount" style to the first paragraph in the sample file named ApplyStyleToParagraph.docx.
string filename = @"C:\Users\Public\Documents\ApplyStyleToParagraph.docx";
using (WordprocessingDocument doc =
WordprocessingDocument.Open(filename, true))
{
// Get the first paragraph.
Paragraph p =
doc.MainDocumentPart.Document.Body.Descendants<Paragraph>()
.ElementAtOrDefault(1);
// Check for a null reference.
if (p == null)
{
throw new ArgumentOutOfRangeException("p",
"Paragraph was not found.");
}
ApplyStyleToParagraph(doc, "OverdueAmount", "Overdue Amount", p);
}
The following is the complete code sample in both C# and Visual Basic.
using DocumentFormat.OpenXml;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Wordprocessing;
using System;
using System.Linq;
// Apply a style to a paragraph.
static void ApplyStyleToParagraph(WordprocessingDocument doc, string styleid, string stylename, Paragraph p)
{
if (doc is null)
{
throw new ArgumentNullException(nameof(doc));
}
// If the paragraph has no ParagraphProperties object, create one.
if (p.Elements<ParagraphProperties>().Count() == 0)
{
p.PrependChild<ParagraphProperties>(new ParagraphProperties());
}
// Get the paragraph properties element of the paragraph.
ParagraphProperties pPr = p.Elements<ParagraphProperties>().First();
// Get the Styles part for this document.
StyleDefinitionsPart? part = doc.MainDocumentPart?.StyleDefinitionsPart;
// If the Styles part does not exist, add it and then add the style.
if (part is null)
{
part = AddStylesPartToPackage(doc);
AddNewStyle(part, styleid, stylename);
}
else
{
// If the style is not in the document, add it.
if (IsStyleIdInDocument(doc, styleid) != true)
{
// No match on styleid, so let's try style name.
string? styleidFromName = GetStyleIdFromStyleName(doc, stylename);
if (styleidFromName is null)
{
AddNewStyle(part, styleid, stylename);
}
else
styleid = styleidFromName;
}
}
// Set the style of the paragraph.
pPr.ParagraphStyleId = new ParagraphStyleId() { Val = styleid };
}
// Return true if the style id is in the document, false otherwise.
static bool IsStyleIdInDocument(WordprocessingDocument doc, string styleid)
{
// Get access to the Styles element for this document.
Styles? s = doc.MainDocumentPart?.StyleDefinitionsPart?.Styles;
if (s is null)
{
return false;
}
// Check that there are styles and how many.
int n = s.Elements<Style>().Count();
if (n == 0)
{
return false;
}
// Look for a match on styleid.
Style? style = s.Elements<Style>()
.Where(st => (st.StyleId is not null && st.StyleId == styleid) && (st.Type is not null && st.Type == StyleValues.Paragraph))
.FirstOrDefault();
if (style is null)
{
return false;
}
return true;
}
// Return styleid that matches the styleName, or null when there's no match.
static string? GetStyleIdFromStyleName(WordprocessingDocument doc, string styleName)
{
StyleDefinitionsPart? stylePart = doc.MainDocumentPart?.StyleDefinitionsPart;
string? styleId = stylePart?.Styles?.Descendants<StyleName>()
.Where(s =>
{
OpenXmlElement? p = s.Parent;
EnumValue<StyleValues>? styleValue = p is null ? null : ((Style)p).Type;
return s.Val is not null && s.Val.Value is not null && s.Val.Value.Equals(styleName) &&
(styleValue is not null && styleValue == StyleValues.Paragraph);
})
.Select(n =>
{
OpenXmlElement? p = n.Parent;
return p is null ? null : ((Style)p).StyleId;
}).FirstOrDefault();
return styleId;
}
// Create a new style with the specified styleid and stylename and add it to the specified
// style definitions part.
static void AddNewStyle(StyleDefinitionsPart styleDefinitionsPart, string styleid, string stylename)
{
// Get access to the root element of the styles part.
styleDefinitionsPart.Styles ??= new Styles();
Styles styles = styleDefinitionsPart.Styles;
// Create a new paragraph style and specify some of the properties.
Style style = new Style()
{
Type = StyleValues.Paragraph,
StyleId = styleid,
CustomStyle = true
};
StyleName styleName1 = new StyleName() { Val = stylename };
BasedOn basedOn1 = new BasedOn() { Val = "Normal" };
NextParagraphStyle nextParagraphStyle1 = new NextParagraphStyle() { Val = "Normal" };
style.Append(styleName1);
style.Append(basedOn1);
style.Append(nextParagraphStyle1);
// Create the StyleRunProperties object and specify some of the run properties.
StyleRunProperties styleRunProperties1 = new StyleRunProperties();
Bold bold1 = new Bold();
Color color1 = new Color() { ThemeColor = ThemeColorValues.Accent2 };
RunFonts font1 = new RunFonts() { Ascii = "Lucida Console" };
Italic italic1 = new Italic();
// Specify a 12 point size.
FontSize fontSize1 = new FontSize() { Val = "24" };
styleRunProperties1.Append(bold1);
styleRunProperties1.Append(color1);
styleRunProperties1.Append(font1);
styleRunProperties1.Append(fontSize1);
styleRunProperties1.Append(italic1);
// Add the run properties to the style.
style.Append(styleRunProperties1);
// Add the style to the styles part.
styles.Append(style);
}
// Add a StylesDefinitionsPart to the document. Returns a reference to it.
static StyleDefinitionsPart AddStylesPartToPackage(WordprocessingDocument doc)
{
MainDocumentPart mainDocumentPart = doc.MainDocumentPart ?? doc.AddMainDocumentPart();
StyleDefinitionsPart part = mainDocumentPart.AddNewPart<StyleDefinitionsPart>();
Styles root = new Styles();
root.Save(part);
return part;
}