Overview
This implementation demonstrates how to programmatically apply “styles,” such as headings or emphasis, to a Word document. When handling styles in OpenXML, simply specifying a Style ID is insufficient; the style definition itself must exist within the document’s internal structure (styles.xml). This article introduces a safe method to “add the definition if it does not exist, then apply it to the paragraph.”
Specifications (Input/Output)
- Input: – Target Word file path.
- Style ID (e.g., “MyCustomStyle”).
- Style Display Name (e.g., “My Custom Style”).
- Target paragraph object.
- Output: A Word document with the style applied.
- Library: DocumentFormat.OpenXml (NuGet package).
Implemented Methods
| Method Name | Description |
| AddStyleIfNotDefined | Checks if the specified Style ID exists in the document; if not, creates and registers it. |
| ApplyStyleToParagraph | Manipulates paragraph properties to assign a specific Style ID. |
Basic Usage
using var editor = new MyWordStyleEditor("StyleDoc.docx");
// 1. Ensure the style definition exists (creates a blue, bold style if missing)
editor.AddStyleIfNotDefined("BlueHeader", "Blue Header Style");
// 2. Create and add a paragraph
var para = editor.AppendParagraph("Apply a custom style to this line.");
// 3. Apply the style
editor.ApplyStyleToParagraph(para, "BlueHeader");
Full Code Example
using System;
using System.IO;
using System.Linq;
using DocumentFormat.OpenXml;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Wordprocessing;
class Program
{
static void Main()
{
string filePath = "StyledDocument.docx";
// Create a dummy file for testing
CreateDummyFile(filePath);
try
{
Console.WriteLine($"Opening {filePath}...");
using var editor = new MyWordStyleEditor(filePath);
// 1. Add a custom style definition (ID: CustomTitle, Name: Custom Title)
// It will only be created if it does not already exist
editor.AddStyleIfNotDefined("CustomTitle", "Custom Title");
// 2. Add a standard paragraph
editor.AppendParagraph("This is normal text.");
// 3. Add a paragraph to be styled
var styledPara = editor.AppendParagraph("This is text with a style applied.");
// 4. Apply the style
editor.ApplyStyleToParagraph(styledPara, "CustomTitle");
Console.WriteLine("Style applied and saved.");
}
catch (Exception ex)
{
Console.WriteLine($"Error: {ex.Message}");
}
}
static void CreateDummyFile(string path)
{
using var doc = WordprocessingDocument.Create(path, WordprocessingDocumentType.Document);
var main = doc.AddMainDocumentPart();
main.Document = new Document(new Body());
}
}
/// <summary>
/// Class for performing Word style operations
/// </summary>
public sealed class MyWordStyleEditor : IDisposable
{
private WordprocessingDocument _document;
private Body _body;
public MyWordStyleEditor(string filePath)
{
_document = WordprocessingDocument.Open(filePath, true);
_body = _document.MainDocumentPart.Document.Body;
}
public void Dispose()
{
_document?.Dispose();
}
/// <summary>
/// Checks if a style with the specified ID exists in the document; if not, creates and adds it.
/// </summary>
public void AddStyleIfNotDefined(string styleId, string styleName)
{
// Retrieve the StyleDefinitionsPart (create it if missing)
var stylePart = _document.MainDocumentPart.StyleDefinitionsPart
?? _document.MainDocumentPart.AddNewPart<StyleDefinitionsPart>();
// Retrieve the Styles root element (create it if missing)
if (stylePart.Styles == null)
{
stylePart.Styles = new Styles();
stylePart.Styles.Save();
}
// Check if the ID already exists
if (stylePart.Styles.Elements<Style>().Any(s => s.StyleId == styleId))
{
return; // Do nothing as it already exists
}
// --- Create a new style ---
var style = new Style()
{
Type = StyleValues.Paragraph,
StyleId = styleId,
CustomStyle = true
};
style.Append(new StyleName() { Val = styleName });
style.Append(new BasedOn() { Val = "Normal" });
style.Append(new NextParagraphStyle() { Val = "Normal" });
// Formatting (e.g., Blue text, Bold, Size 24pt)
var rPr = new StyleRunProperties();
rPr.Append(new Color() { Val = "0000FF" }); // Blue
rPr.Append(new Bold()); // Bold
rPr.Append(new FontSize() { Val = "48" }); // 24pt (Specified in half-points, so 48)
style.Append(rPr);
// Append to Styles and save
stylePart.Styles.Append(style);
stylePart.Styles.Save();
}
/// <summary>
/// Applies a style ID to the specified paragraph
/// </summary>
public void ApplyStyleToParagraph(Paragraph p, string styleId)
{
// Create properties if they do not exist
if (p.ParagraphProperties == null)
{
p.ParagraphProperties = new ParagraphProperties();
}
// Set the Style ID
p.ParagraphProperties.ParagraphStyleId = new ParagraphStyleId() { Val = styleId };
}
// Helper: Add paragraph
public Paragraph AppendParagraph(string text)
{
var p = new Paragraph(new Run(new Text(text)));
_body.AppendChild(p);
return p;
}
}
Customization Points
- Modifying Style Definitions: You can customize the look by changing the settings in
StyleRunPropertieswithin theAddStyleIfNotDefinedmethod.- Color: Text color in hex RGB.
- FontSize: Text size in half-points (e.g., “48” for 24pt).
- Underline: Text underlining.
- Setting Paragraph-Level Styles: In addition to text decoration (
RunProperties), you can include paragraph-level settings (ParagraphProperties) in a style, such asJustification(alignment) orSpacingBetweenLines.
Important Notes
- Difference Between StyleId and StyleName:
StyleIdis the identifier used programmatically (recommended to have no spaces), whileStyleNameis the name visible to the user in Word’s UI. UseStyleIdinApplyStyleToParagraph. - Missing StyleDefinitionsPart: When creating a blank document from scratch, the
StyleDefinitionsPart(styles.xml) might not exist. The logic usingAddNewPartas shown in the example is required. - Built-in Styles: Built-in styles like “Heading1” may not have definitions included in every document. You may still need to add the definition, although built-in definitions can be complex. Using a Word template is often more efficient for complex styles.
Advanced Usage
Searching and Applying an Existing Style
If the ID is unknown, you can search for and apply a style using its display name.
public void ApplyStyleByName(Paragraph p, string styleName)
{
var styles = _document.MainDocumentPart.StyleDefinitionsPart?.Styles;
if (styles == null) return;
// Search for a style with a matching name
var style = styles.Elements<Style>()
.FirstOrDefault(s => s.StyleName != null && s.StyleName.Val == styleName);
if (style != null)
{
ApplyStyleToParagraph(p, style.StyleId);
}
}
Summary
Applying styles in OpenXML is a two-step process: “registering the style definition (AddStyle)” and “linking it to the paragraph (ApplyStyle).” While the XML structure for style definitions can be complex, encapsulating the “check existence then add” logic into a class keeps your primary processing code clean and maintainable.
