Overview
This implementation demonstrates how to open an existing Word file (.docx) and insert a new paragraph “before” a specified reference paragraph. In the OpenXML SDK, you can interject new elements at any position within the document structure by using the InsertBefore method of the Body element.
Specifications (Input/Output)
- Input: Target file path, the reference paragraph object for the insertion point, and the text to be inserted.
- Output: An edited and saved
.docxfile. - Library: DocumentFormat.OpenXml (NuGet package).
Implemented Methods
| Method Name | Description |
| Open | Opens an existing Word file in “edit mode” and creates a document manipulation instance. |
| GetParagraphs | Retrieves all paragraphs located directly under the document (Body). |
| InsertParagraph | Inserts a new text paragraph immediately before the specified paragraph. |
Basic Usage
// Open in edit mode
using var doc = MyWordEditor.Open("Report.docx");
// Get the second paragraph (index 1)
var targetParams = doc.GetParagraphs().ElementAtOrDefault(1);
if (targetParams != null)
{
// Insert "before" the retrieved paragraph
doc.InsertParagraph(targetParams, "[Addition] A new line will be inserted here.");
}
Full Code Example
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using DocumentFormat.OpenXml;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Wordprocessing;
class Program
{
static void Main()
{
string filePath = "ExistingDocument.docx";
// Create the file for testing if it does not exist
EnsureFileExists(filePath);
try
{
Console.WriteLine($"Opening {filePath} ...");
// 1. Open the existing file
using var editor = MyWordEditor.Open(filePath);
// 2. Identify the insertion point
// Here, we retrieve the "second paragraph (Index 1)"
var paragraphs = editor.GetParagraphs().ToList();
var targetParagraph = paragraphs.ElementAtOrDefault(1);
if (targetParagraph != null)
{
// 3. Insert before the specified paragraph
editor.InsertParagraph(targetParagraph, ">>> Interjected Text <<<");
Console.WriteLine("Inserted text before paragraph 2.");
}
else
{
// Append to the end if the target is not found
editor.AppendParagraph(">>> Text added to the end <<<");
Console.WriteLine("Target not found. Appended text instead.");
}
// Automatically saved upon Disposal
}
catch (Exception ex)
{
Console.WriteLine($"Error: {ex.Message}");
}
}
// For creating test data
static void EnsureFileExists(string path)
{
if (!File.Exists(path))
{
using var doc = WordprocessingDocument.Create(path, WordprocessingDocumentType.Document);
var mainPart = doc.AddMainDocumentPart();
mainPart.Document = new Document(new Body());
var body = mainPart.Document.Body;
// Populate with some initial data
body.AppendChild(new Paragraph(new Run(new Text("1. First Line"))));
body.AppendChild(new Paragraph(new Run(new Text("2. Second Line"))));
body.AppendChild(new Paragraph(new Run(new Text("3. Third Line"))));
}
}
}
// Word editing class using OpenXML SDK
public sealed class MyWordEditor : IDisposable
{
private WordprocessingDocument _document;
private Body _body;
private MyWordEditor() { }
// Factory method to open an existing file
public static MyWordEditor Open(string filePath)
{
var editor = new MyWordEditor();
// Open in edit mode by setting the second argument isEditable to true
editor._document = WordprocessingDocument.Open(filePath, true);
// Maintain a reference to the Body element
editor._body = editor._document.MainDocumentPart.Document.Body;
return editor;
}
// Retrieve a list of paragraphs directly under the Body
public IEnumerable<Paragraph> GetParagraphs()
{
// Elements<Paragraph>() retrieves only immediate child elements
// Use Descendants<Paragraph>() if you want to include paragraphs inside tables, etc.
return _body.Elements<Paragraph>();
}
// Insert immediately before the specified paragraph
public void InsertParagraph(Paragraph target, string text)
{
// Create new paragraph structure
var newPara = new Paragraph(
new Run(
new Text(text)
)
);
// Use the OpenXML InsertBefore method
_body.InsertBefore(newPara, target);
}
// Append to the end of the document (Helper)
public void AppendParagraph(string text)
{
var newPara = new Paragraph(
new Run(
new Text(text)
)
);
_body.AppendChild(newPara);
}
public void Dispose()
{
// Commit changes and close
_document?.Dispose();
}
}
Customization Points
- Inserting Immediately After: By using the
InsertAftermethod instead ofInsertBefore, you can add a line below the specified paragraph. - Search and Insert: You can filter
GetParagraphs()using LINQ to find a paragraph containing a “specific keyword” and insert text before or after it.- Example:
var target = editor.GetParagraphs().FirstOrDefault(p => p.InnerText.Contains("Insert here"));
- Example:
- Applying Styles: By setting
ParagraphPropertieson the insertedParagraphobject, you can format the inserted text as “bold” or “red text,” for example.
Important Notes
- Edit Permissions (isEditable): You must set the second argument of
WordprocessingDocument.Opentotrue; otherwise, it will open in read-only mode, and an exception will occur during saving. - Element Hierarchy: When using
Elements<Paragraph>()inGetParagraphs, paragraphs within tables will not be retrieved. UseDescendants<Paragraph>()to search inside tables. However, be cautious as inserting directly into a table may disrupt the layout. - Index Range: When retrieving via
ElementAtOrDefault(1),nullwill be returned if the document has too few lines. Always perform a null check before executing the insertion process.
Advanced Usage
Removing Paragraphs Containing Specific Text
This function allows you to remove unwanted paragraphs in addition to inserting them.
public void RemoveParagraphsByText(string keyword)
{
// Create a list of targets to prevent errors during enumeration
var targets = _body.Elements<Paragraph>()
.Where(p => p.InnerText.Contains(keyword))
.ToList();
foreach (var p in targets)
{
p.Remove();
}
}
Conclusion
When editing an existing Word document, open it with WordprocessingDocument.Open(path, true) and access paragraph objects through the Body property. The InsertBefore method functions similarly to inserting into a list structure, allowing you to freely modify the document structure once the reference paragraph is identified.
