Overview
This implementation creates .docx Word files in environments where Microsoft Word is not installed (such as servers or Linux). Using the “OpenXML SDK” provided by Microsoft, we programmatically construct the Word document structure—comprising the Document, Body, Paragraph, Run, and Text elements—to output text.
Specifications (Input/Output)
- Input: Destination file path, text to be written.
- Output: A
.docxfile. - Library: DocumentFormat.OpenXml (NuGet package).
Implemented Methods
| Method Name | Description |
| AppendParagraph | Basic implementation that creates and adds a Paragraph, Run, and Text in sequence. |
| AppendParagraph2 | A shorthand implementation that adds a paragraph concisely using nested constructors. |
Basic Usage
First, install the NuGet package:
dotnet add package DocumentFormat.OpenXml
Example usage of the implemented class:
// Initialize instance with file path
using var doc = new MyWordDoc("Report.docx");
// Write using the standard method
doc.AppendParagraph("This is the first paragraph.");
// Write using the shorthand method
doc.AppendParagraph2("This is the next paragraph, written concisely.");
Full Code Example
The following code demonstrates how to wrap OpenXML SDK functionality for document generation.
using System;
using DocumentFormat.OpenXml;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Wordprocessing;
class Program
{
static void Main()
{
string filePath = "Report.docx";
try
{
Console.WriteLine($"Creating Word document: {filePath}");
// Ensure Dispose (Save/Close) via the using statement
using (var doc = new MyWordDoc(filePath))
{
doc.AppendParagraph("[Daily Report] Today's Work Summary");
doc.AppendParagraph("Today, I implemented the basic logic for OpenXML SDK.");
doc.AppendParagraph2("Progress is on track.");
doc.AppendParagraph2("Regards.");
}
Console.WriteLine("Document created successfully.");
}
catch (Exception ex)
{
Console.WriteLine($"Error: {ex.Message}");
}
}
}
/// <summary>
/// Class for creating Word documents using OpenXML SDK
/// </summary>
public sealed class MyWordDoc : IDisposable
{
private WordprocessingDocument _document;
private Body _body;
// Initialize document structure in the constructor
public MyWordDoc(string filePath)
{
// 1. Create WordprocessingDocument (File creation)
_document = WordprocessingDocument.Create(filePath, WordprocessingDocumentType.Document);
// 2. Add MainDocumentPart (The primary part of the document)
MainDocumentPart mainPart = _document.AddMainDocumentPart();
// 3. Create the Document tree
mainPart.Document = new Document();
// 4. Create Body (Main content area) and add to Document
_body = mainPart.Document.AppendChild(new Body());
}
// Standard method (Clear structural representation)
public void AppendParagraph(string text)
{
// Create Paragraph
Paragraph para = _body.AppendChild(new Paragraph());
// Create Run and add to Paragraph
// Note: A Run represents a region with specific formatting (font, color, etc.)
Run run = para.AppendChild(new Run());
// Create Text and add to Run
run.AppendChild(new Text(text));
}
// Concise method (Using nested constructors)
public void AppendParagraph2(string text)
{
// Generate Paragraph > Run > Text as a batch and add to Body
_body.AppendChild(
new Paragraph(
new Run(
new Text(text)
)
)
);
}
// Resource release and saving
public void Dispose()
{
// Changes are committed to the file when Disposed (or Closed)
_document?.Dispose();
}
}
Customization Points
- Applying Styles: To make text bold or change colors, set
RunPropertieson theRunobject.C#var runProps = new RunProperties(); runProps.Append(new Bold()); // Bold runProps.Append(new Color() { Val = "FF0000" }); // Red run.RunProperties = runProps; - Inserting Page Breaks: To force a page break, add
new Break() { Type = BreakValues.Page }inside aParagraph.
Important Notes
- Hierarchical Understanding: OpenXML strictly enforces a hierarchy of Body > Paragraph > Run > Text. You cannot add Text directly to the Body; you must follow this nested structure.
- Necessity of Dispose: The
WordprocessingDocumentwrites XML data to the file and completes the save only whenDispose()(orClose()) is called. Without theusingblock, the file may be corrupted or result in a 0-byte size. - File Access Conflicts: If the target file is currently open in Microsoft Word, an
IOExceptionwill occur. Ensure the file is closed before execution.
Advanced Usage
Implementing Text Replacement
This example shows how to replace specific placeholders (e.g., {DATE}) within a template file.
public void ReplaceText(string placeholder, string value)
{
// Search for all text elements within the document body
foreach (var text in _body.Descendants<Text>())
{
if (text.Text.Contains(placeholder))
{
text.Text = text.Text.Replace(placeholder, value);
}
}
}
Conclusion
Using the OpenXML SDK, you can generate documents quickly and safely without depending on the Word application. While the structure may appear verbose at first, understanding the relationship between the Paragraph and the Run allows you to encapsulate methods for intuitive document manipulation.
